opd_image.c revision cc2ee177dbb3befca43e36cfc56778b006c3d050
1/**
2 * @file opd_image.c
3 * Management of binary images
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author John Levon
9 * @author Philippe Elie
10 */
11
12#include "opd_image.h"
13#include "opd_printf.h"
14#include "opd_sample_files.h"
15#include "opd_24_stats.h"
16#include "oprofiled.h"
17
18#include "op_file.h"
19#include "op_config_24.h"
20#include "op_libiberty.h"
21#include "op_string.h"
22
23#include <string.h>
24#include <stdlib.h>
25#include <stdio.h>
26
27/* maintained for statistics purpose only */
28static int nr_images;
29
30/* list of images */
31#define OPD_IMAGE_HASH_SIZE 2048
32static struct list_head opd_images[OPD_IMAGE_HASH_SIZE];
33
34
35void opd_init_images(void)
36{
37	int i;
38	for (i = 0; i < OPD_IMAGE_HASH_SIZE; ++i) {
39		list_init(&opd_images[i]);
40	}
41}
42
43
44int opd_get_nr_images(void)
45{
46	return nr_images;
47}
48
49
50void opd_delete_image(struct opd_image * image)
51{
52	verbprintf(vmisc, "Deleting image: name %s app_name %s, kernel %d, "
53	           "tid %d, tgid %d ref count %u\n",
54	           image->name, image->app_name, image->kernel,
55	           image->tid, image->tgid, (int)image->ref_count);
56
57	if (image->ref_count <= 0) {
58		printf("image->ref_count < 0 for image: name %s app_name %s, "
59		       "kernel %d, tid %d, tgid %d ref count %u\n",
60		       image->name, image->app_name, image->kernel,
61		       image->tid, image->tgid, image->ref_count);
62		abort();
63	}
64
65	if (--image->ref_count != 0)
66		return;
67
68	if (image->name)
69		free(image->name);
70	if (image->app_name)
71		free(image->app_name);
72	list_del(&image->hash_next);
73	opd_close_image_samples_files(image);
74	free(image);
75
76	nr_images--;
77}
78
79
80void opd_for_each_image(opd_image_cb image_cb)
81{
82	struct list_head * pos;
83	struct list_head * pos2;
84	int i;
85
86	for (i = 0; i < OPD_IMAGE_HASH_SIZE; ++i) {
87		list_for_each_safe(pos, pos2, &opd_images[i]) {
88			struct opd_image * image =
89				list_entry(pos, struct opd_image, hash_next);
90			image_cb(image);
91		}
92	}
93}
94
95
96/**
97 * opd_hash_image - hash an image
98 * @param hash  hash of image name
99 * @param tid  thread id
100 * @param tgid  thread group id
101 *
102 * return the hash code for the passed parameters
103 */
104static size_t opd_hash_image(char const * name, pid_t tid, pid_t tgid)
105{
106	size_t hash = op_hash_string(name);
107	if (separate_thread)
108		hash += tid + tgid;
109	return  hash % OPD_IMAGE_HASH_SIZE;
110}
111
112
113/**
114 * opd_new_image - create an image sample file
115 * @param app_name  the application name where belongs this image
116 * @param name  name of the image to add
117 * @param kernel  is the image a kernel/module image
118 * @param tid  thread id
119 * @param tgid  thread group id
120 *
121 * image at funtion entry is uninitialised
122 * name is copied i.e. should be GC'd separately from the
123 * image structure if appropriate.
124 *
125 * Initialise an opd_image struct for the image image
126 * without opening the associated samples files. At return
127 * the image is fully initialized.
128 */
129static struct opd_image *
130opd_new_image(char const * name, char const * app_name, int kernel,
131              pid_t tid, pid_t tgid)
132{
133	size_t hash_image;
134	struct opd_image * image;
135
136	verbprintf(vmisc, "Creating image: %s %s, kernel %d, tid %d, "
137	           "tgid %d\n", name, app_name, kernel, tid, tgid);
138
139	image = xmalloc(sizeof(struct opd_image));
140
141	list_init(&image->hash_next);
142	image->name = xstrdup(name);
143	image->kernel = kernel;
144	image->tid = tid;
145	image->tgid = tgid;
146	image->ref_count = 0;
147	image->app_name = app_name ? xstrdup(app_name) : NULL;
148	image->mtime = op_get_mtime(image->name);
149
150	image->ignored = 1;
151	if (separate_lib && app_name)
152		image->ignored = is_image_ignored(app_name);
153	if (image->ignored)
154		image->ignored = is_image_ignored(name);
155
156	memset(image->sfiles, '\0', NR_CPUS * sizeof(struct opd_24_sfile **));
157
158	hash_image = opd_hash_image(name, tid, tgid);
159	list_add(&image->hash_next, &opd_images[hash_image]);
160
161	nr_images++;
162
163	return image;
164}
165
166
167/**
168 * is_same_image - check for identical image
169 * @param image  image to compare
170 * @param name  name of image
171 * @param app_name image must belong to this application name
172 * @param tid  thread id
173 * @param tgid  thread group id
174 *
175 * on entry caller have checked than strcmp(image->name, name) == 0
176 * return 0 if the couple (name, app_name) refers to same image
177 */
178static int is_same_image(struct opd_image const * image, char const * app_name,
179                         pid_t tid, pid_t tgid)
180{
181	/* correctness is really important here, if we fail to recognize
182	 * identical image we will open/mmap multiple time the same samples
183	 * files which is not supported by the kernel, strange assertion
184	 * failure in libfd is a typical symptom of that */
185
186	if (separate_thread) {
187		if (image->tid != tid || image->tgid != tgid)
188			return 1;
189	}
190
191	/* if !separate_lib, the comparison made by caller is enough */
192	if (!separate_lib)
193		return 0;
194
195	if (image->app_name == NULL && app_name == NULL)
196		return 0;
197
198	if (image->app_name != NULL && app_name != NULL &&
199	    !strcmp(image->app_name, app_name))
200		return 0;
201
202	/* /proc parsed image come with a non null app_name but notification
203	 * for application itself come with a null app_name, in this case
204	 * the test above fail so check for this case. */
205	if (image->app_name && !app_name && !strcmp(image->app_name, image->name))
206		return 0;
207
208	return 1;
209}
210
211
212/**
213 * opd_find_image - find an image
214 * @param name  name of image to find
215 * @param hash  hash of image to find
216 * @param app_name  the application name where belongs this image
217 * @param tid  thread id
218 * @param tgid  thread group id
219 *
220 * Returns the image pointer for the file specified by name, or %NULL.
221 */
222static struct opd_image * opd_find_image(char const * name,
223                                char const * app_name, pid_t tid, pid_t tgid)
224{
225	/* suppress uninitialized use warning */
226	struct opd_image * image = 0;
227	struct list_head * pos;
228	size_t bucket;
229
230	opd_24_stats[OPD_IMAGE_HASH_ACCESS]++;
231	bucket = opd_hash_image(name, tid, tgid);
232	list_for_each(pos, &opd_images[bucket]) {
233		opd_24_stats[OPD_IMAGE_HASH_DEPTH]++;
234		image = list_entry(pos, struct opd_image, hash_next);
235
236		if (!strcmp(image->name, name)) {
237			if (!is_same_image(image, app_name, tid, tgid))
238				break;
239		}
240	}
241
242	if (pos == &opd_images[bucket])
243		return NULL;
244
245	/* The app_name field is always valid */
246	return image;
247}
248
249
250struct opd_image * opd_get_image(char const * name, char const * app_name,
251                                 int kernel, pid_t tid, pid_t tgid)
252{
253	struct opd_image * image;
254	if ((image = opd_find_image(name, app_name, tid, tgid)) == NULL)
255		image = opd_new_image(name, app_name, kernel, tid, tgid);
256
257	return image;
258}
259
260
261struct opd_image * opd_get_kernel_image(char const * name,
262                               char const * app_name, pid_t tid, pid_t tgid)
263{
264	return opd_get_image(name, app_name, 1, tid, tgid);
265}
266