/**
* @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);
}