| /** |
| * @file opd_image.c |
| * Management of binary images |
| * |
| * @remark Copyright 2002 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author John Levon |
| * @author Philippe Elie |
| */ |
| |
| #include "opd_image.h" |
| #include "opd_printf.h" |
| #include "opd_sample_files.h" |
| #include "opd_24_stats.h" |
| #include "oprofiled.h" |
| |
| #include "op_file.h" |
| #include "op_config_24.h" |
| #include "op_libiberty.h" |
| #include "op_string.h" |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| /* maintained for statistics purpose only */ |
| static int nr_images; |
| |
| /* list of images */ |
| #define OPD_IMAGE_HASH_SIZE 2048 |
| static struct list_head opd_images[OPD_IMAGE_HASH_SIZE]; |
| |
| |
| void opd_init_images(void) |
| { |
| int i; |
| for (i = 0; i < OPD_IMAGE_HASH_SIZE; ++i) |
| list_init(&opd_images[i]); |
| } |
| |
| |
| int opd_get_nr_images(void) |
| { |
| return nr_images; |
| } |
| |
| |
| void opd_delete_image(struct opd_image * image) |
| { |
| verbprintf(vmisc, "Deleting image: name %s app_name %s, kernel %d, " |
| "tid %d, tgid %d ref count %u\n", |
| image->name, image->app_name, image->kernel, |
| image->tid, image->tgid, (int)image->ref_count); |
| |
| if (image->ref_count <= 0) { |
| printf("image->ref_count < 0 for image: name %s app_name %s, " |
| "kernel %d, tid %d, tgid %d ref count %u\n", |
| image->name, image->app_name, image->kernel, |
| image->tid, image->tgid, image->ref_count); |
| abort(); |
| } |
| |
| if (--image->ref_count != 0) |
| return; |
| |
| if (image->name) |
| free(image->name); |
| if (image->app_name) |
| free(image->app_name); |
| list_del(&image->hash_next); |
| opd_close_image_samples_files(image); |
| free(image); |
| |
| nr_images--; |
| } |
| |
| |
| void opd_for_each_image(opd_image_cb image_cb) |
| { |
| struct list_head * pos; |
| struct list_head * pos2; |
| int i; |
| |
| for (i = 0; i < OPD_IMAGE_HASH_SIZE; ++i) { |
| list_for_each_safe(pos, pos2, &opd_images[i]) { |
| struct opd_image * image = |
| list_entry(pos, struct opd_image, hash_next); |
| image_cb(image); |
| } |
| } |
| } |
| |
| |
| /** |
| * opd_hash_image - hash an image |
| * @param hash hash of image name |
| * @param tid thread id |
| * @param tgid thread group id |
| * |
| * return the hash code for the passed parameters |
| */ |
| static size_t opd_hash_image(char const * name, pid_t tid, pid_t tgid) |
| { |
| size_t hash = op_hash_string(name); |
| if (separate_thread) |
| hash += tid + tgid; |
| return hash % OPD_IMAGE_HASH_SIZE; |
| } |
| |
| |
| /** |
| * opd_new_image - create an image sample file |
| * @param app_name the application name where belongs this image |
| * @param name name of the image to add |
| * @param kernel is the image a kernel/module image |
| * @param tid thread id |
| * @param tgid thread group id |
| * |
| * image at funtion entry is uninitialised |
| * name is copied i.e. should be GC'd separately from the |
| * image structure if appropriate. |
| * |
| * Initialise an opd_image struct for the image image |
| * without opening the associated samples files. At return |
| * the image is fully initialized. |
| */ |
| static struct opd_image * |
| opd_new_image(char const * name, char const * app_name, int kernel, |
| pid_t tid, pid_t tgid) |
| { |
| size_t hash_image; |
| struct opd_image * image; |
| |
| verbprintf(vmisc, "Creating image: %s %s, kernel %d, tid %d, " |
| "tgid %d\n", name, app_name, kernel, tid, tgid); |
| |
| image = xmalloc(sizeof(struct opd_image)); |
| |
| list_init(&image->hash_next); |
| image->name = xstrdup(name); |
| image->kernel = kernel; |
| image->tid = tid; |
| image->tgid = tgid; |
| image->ref_count = 0; |
| image->app_name = app_name ? xstrdup(app_name) : NULL; |
| image->mtime = op_get_mtime(image->name); |
| |
| image->ignored = 1; |
| if (separate_lib && app_name) |
| image->ignored = is_image_ignored(app_name); |
| if (image->ignored) |
| image->ignored = is_image_ignored(name); |
| |
| memset(image->sfiles, '\0', NR_CPUS * sizeof(struct opd_24_sfile **)); |
| |
| hash_image = opd_hash_image(name, tid, tgid); |
| list_add(&image->hash_next, &opd_images[hash_image]); |
| |
| nr_images++; |
| |
| return image; |
| } |
| |
| |
| /** |
| * is_same_image - check for identical image |
| * @param image image to compare |
| * @param name name of image |
| * @param app_name image must belong to this application name |
| * @param tid thread id |
| * @param tgid thread group id |
| * |
| * on entry caller have checked than strcmp(image->name, name) == 0 |
| * return 0 if the couple (name, app_name) refers to same image |
| */ |
| static int is_same_image(struct opd_image const * image, char const * app_name, |
| pid_t tid, pid_t tgid) |
| { |
| /* correctness is really important here, if we fail to recognize |
| * identical image we will open/mmap multiple time the same samples |
| * files which is not supported by the kernel, strange assertion |
| * failure in libfd is a typical symptom of that */ |
| |
| if (separate_thread) { |
| if (image->tid != tid || image->tgid != tgid) |
| return 1; |
| } |
| |
| /* if !separate_lib, the comparison made by caller is enough */ |
| if (!separate_lib) |
| return 0; |
| |
| if (image->app_name == NULL && app_name == NULL) |
| return 0; |
| |
| if (image->app_name != NULL && app_name != NULL && |
| !strcmp(image->app_name, app_name)) |
| return 0; |
| |
| /* /proc parsed image come with a non null app_name but notification |
| * for application itself come with a null app_name, in this case |
| * the test above fail so check for this case. */ |
| if (image->app_name && !app_name && !strcmp(image->app_name, image->name)) |
| return 0; |
| |
| return 1; |
| } |
| |
| |
| /** |
| * opd_find_image - find an image |
| * @param name name of image to find |
| * @param hash hash of image to find |
| * @param app_name the application name where belongs this image |
| * @param tid thread id |
| * @param tgid thread group id |
| * |
| * Returns the image pointer for the file specified by name, or %NULL. |
| */ |
| static struct opd_image * opd_find_image(char const * name, |
| char const * app_name, pid_t tid, pid_t tgid) |
| { |
| /* suppress uninitialized use warning */ |
| struct opd_image * image = 0; |
| struct list_head * pos; |
| size_t bucket; |
| |
| opd_24_stats[OPD_IMAGE_HASH_ACCESS]++; |
| bucket = opd_hash_image(name, tid, tgid); |
| list_for_each(pos, &opd_images[bucket]) { |
| opd_24_stats[OPD_IMAGE_HASH_DEPTH]++; |
| image = list_entry(pos, struct opd_image, hash_next); |
| |
| if (!strcmp(image->name, name)) { |
| if (!is_same_image(image, app_name, tid, tgid)) |
| break; |
| } |
| } |
| |
| if (pos == &opd_images[bucket]) |
| return NULL; |
| |
| /* The app_name field is always valid */ |
| return image; |
| } |
| |
| |
| struct opd_image * opd_get_image(char const * name, char const * app_name, |
| int kernel, pid_t tid, pid_t tgid) |
| { |
| struct opd_image * image; |
| if ((image = opd_find_image(name, app_name, tid, tgid)) == NULL) |
| image = opd_new_image(name, app_name, kernel, tid, tgid); |
| |
| return image; |
| } |
| |
| |
| struct opd_image * opd_get_kernel_image(char const * name, |
| char const * app_name, pid_t tid, pid_t tgid) |
| { |
| return opd_get_image(name, app_name, 1, tid, tgid); |
| } |