/** * @file opd_proc.c * Management of processes * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author John Levon * @author Philippe Elie */ #include "op_hw_config.h" #include "opd_proc.h" #include "opd_image.h" #include "opd_mapping.h" #include "opd_sample_files.h" #include "opd_kernel.h" #include "opd_24_stats.h" #include "opd_printf.h" #include "oprofiled.h" #include "op_interface.h" #include "op_libiberty.h" #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> /* size of process hash table */ #define OPD_MAX_PROC_HASH 1024 extern int cpu_number; /* hash of process lists */ static struct list_head opd_procs[OPD_MAX_PROC_HASH]; /* statistics purpose */ static int nr_procs; void opd_init_procs(void) { int i; for (i = 0; i < OPD_MAX_PROC_HASH; i++) list_init(&opd_procs[i]); } int opd_get_nr_procs(void) { return nr_procs; } /** * proc_hash - hash pid value * @param tid pid value to hash * */ inline static uint proc_hash(pid_t tid) { /* FIXME: hash tgid too! */ return ((tid >> 4) ^ (tid)) % OPD_MAX_PROC_HASH; } struct opd_proc * opd_new_proc(pid_t tid, pid_t tgid) { struct opd_proc * proc; nr_procs++; proc = xmalloc(sizeof(struct opd_proc)); list_init(&proc->maps); proc->name = NULL; proc->tid = tid; proc->tgid = tgid; proc->dead = 0; proc->accessed = 0; list_add(&proc->next, &opd_procs[proc_hash(tid)]); return proc; } struct opd_proc * opd_get_proc(pid_t tid, pid_t tgid) { struct opd_proc * proc; uint hash = proc_hash(tid); struct list_head * pos, *pos2; opd_24_stats[OPD_PROC_QUEUE_ACCESS]++; list_for_each_safe(pos, pos2, &opd_procs[hash]) { opd_24_stats[OPD_PROC_QUEUE_DEPTH]++; proc = list_entry(pos, struct opd_proc, next); if (tid == proc->tid && tgid == proc->tgid) { /* LRU to head */ list_del(&proc->next); list_add(&proc->next, &opd_procs[hash]); return proc; } } return NULL; } /** * verb_show_sample - print the sample out to the log * @param offset the offset value * @param map map to print */ inline static void verb_show_sample(unsigned long offset, struct opd_map * map) { verbprintf(vsamples, "DO_PUT_SAMPLE : calc offset 0x%.8lx, " "map start 0x%.8lx, end 0x%.8lx, offset 0x%.8lx, name \"%s\"\n", offset, map->start, map->end, map->offset, map->image->name); } void opd_put_image_sample(struct opd_image * image, unsigned long offset, u32 counter) { struct opd_24_sfile * sfile; int err; if (image->ignored) return; if (!image->sfiles[cpu_number]) { image->sfiles[cpu_number] = xcalloc(OP_MAX_COUNTERS, sizeof(struct op_24_sfile *)); } sfile = image->sfiles[cpu_number][counter]; if (!sfile || !odb_open_count(&sfile->sample_file)) { if (opd_open_24_sample_file(image, counter, cpu_number)) { /* opd_open_24_sample_file output an error message */ opd_24_stats[OPD_LOST_SAMPLEFILE]++; return; } sfile = image->sfiles[cpu_number][counter]; } err = odb_update_node(&sfile->sample_file, offset); if (err) { fprintf(stderr, "%s\n", strerror(err)); abort(); } opd_24_sfile_lru(sfile); } /** * opd_lookup_maps - lookup a proc mappings for a sample * @param proc proc to lookup * @param sample sample to lookup * * iterate through the proc maps searching the mapping which owns sample * if sucessful sample count will be updated and we return non-zero */ static int opd_lookup_maps(struct opd_proc * proc, struct op_sample const * sample) { struct list_head * pos; proc->accessed = 1; opd_24_stats[OPD_MAP_ARRAY_ACCESS]++; list_for_each(pos, &proc->maps) { struct opd_map * map = list_entry(pos, struct opd_map, next); if (opd_is_in_map(map, sample->eip)) { unsigned long offset = opd_map_offset(map, sample->eip); if (map->image != NULL) { verb_show_sample(offset, map); opd_put_image_sample(map->image, offset, sample->counter); } opd_24_stats[OPD_PROCESS]++; return 1; } opd_24_stats[OPD_MAP_ARRAY_DEPTH]++; } return 0; } void opd_put_sample(struct op_sample const * sample) { struct opd_proc * proc; int in_kernel_eip = opd_eip_is_kernel(sample->eip); opd_24_stats[OPD_SAMPLES]++; verbprintf(vsamples, "DO_PUT_SAMPLE: c%d, EIP 0x%.8lx, tgid %.6d pid %.6d\n", sample->counter, sample->eip, sample->tgid, sample->pid); if (!separate_kernel && in_kernel_eip) { opd_handle_kernel_sample(sample->eip, sample->counter); return; } if (!(proc = opd_get_proc(sample->pid, sample->tgid))) { if (in_kernel_eip || no_vmlinux) { /* idle task get a 0 pid and is hidden we can never get * a proc so on we fall back to put sample in vmlinux * or module samples files. Here we will catch also * sample for newly created kernel thread, currently * we can handle properly only kenel thread created * at daemon startup time */ opd_handle_kernel_sample(sample->eip, sample->counter); } else { verbprintf(vmisc, "No proc info for tgid %.6d pid %.6d.\n", sample->tgid, sample->pid); opd_24_stats[OPD_LOST_PROCESS]++; } return; } if (opd_lookup_maps(proc, sample)) return; if (in_kernel_eip) { opd_add_kernel_map(proc, sample->eip); if (opd_lookup_maps(proc, sample)) return; } /* couldn't locate it */ verbprintf(vsamples, "Couldn't find map for pid %.6d, EIP 0x%.8lx.\n", sample->pid, sample->eip); opd_24_stats[OPD_LOST_MAP_PROCESS]++; } void opd_handle_fork(struct op_note const * note) { struct opd_proc * old; struct opd_proc * proc; struct list_head * pos; verbprintf(vmisc, "DO_FORK: from %d, %d to %ld, %ld\n", note->pid, note->tgid, note->addr, note->len); old = opd_get_proc(note->pid, note->tgid); /* we can quite easily get a fork() after the execve() because the * notifications are racy. In particular, the fork notification is * done on parent return (so we know the pid), but this will often be * after the execve is done by the child. * * So we only create a new setup if it doesn't exist already, allowing * both the clone() and the execve() cases to work. */ if (opd_get_proc(note->addr, note->len)) return; /* eip/len is actually tid/tgid of new process */ proc = opd_new_proc(note->addr, note->len); if (!old) return; /* copy the maps */ list_for_each(pos, &old->maps) { struct opd_map * map = list_entry(pos, struct opd_map, next); if (!separate_thread) { opd_add_mapping(proc, map->image, map->start, map->offset, map->end); } else { /* when separating thread we can't create blindly a new * image e.g. pid re-use, multiple mapping with the * same mapping name etc. */ struct opd_image * image = opd_get_image(map->image->name, old->name, map->image->kernel, note->addr, note->len); opd_add_mapping(proc, image, map->start, map->offset, map->end); } } } void opd_handle_exec(pid_t tid, pid_t tgid) { struct opd_proc * proc; verbprintf(vmisc, "DO_EXEC: pid %u %u\n", tid, tgid); /* There is a race for samples received between fork/exec sequence. * These samples belong to the old mapping but we can not say if * samples has been received before the exec or after. This explains * the message "Couldn't find map for ..." in verbose mode. * * Unhappily, it is difficult to get an estimation of these misplaced * samples, the error message can count only out of mapping samples but * not samples between the race and inside the mapping of the exec'ed * process :/. * * Trying to save old mapping is not correct due the above reason. The * only manner to handle this is to flush the module samples hash table * after each fork which is unacceptable for performance reasons */ proc = opd_get_proc(tid, tgid); if (proc) { opd_kill_maps(proc); /* proc->name will be set when the next mapping occurs */ free((char *)proc->name); proc->name = NULL; } else { opd_new_proc(tid, tgid); } } void opd_handle_exit(struct op_note const * note) { struct opd_proc * proc; verbprintf(vmisc, "DO_EXIT: process %d\n", note->pid); proc = opd_get_proc(note->pid, note->tgid); if (proc) { proc->dead = 1; proc->accessed = 1; } else { verbprintf(vmisc, "unknown proc %u just exited.\n", note->pid); } } typedef void (*opd_proc_cb)(struct opd_proc *); /** * @param proc_cb callback to apply onto each existing proc struct * * the callback receive a struct opd_proc * (not a const struct) and is * allowed to freeze the proc struct itself. */ static void opd_for_each_proc(opd_proc_cb proc_cb) { struct list_head * pos; struct list_head * pos2; int i; for (i = 0; i < OPD_MAX_PROC_HASH; ++i) { list_for_each_safe(pos, pos2, &opd_procs[i]) { struct opd_proc * proc = list_entry(pos, struct opd_proc, next); proc_cb(proc); } } } /** * opd_delete_proc - delete a process * @param proc process to delete * * Remove the process proc from the process list and free * the associated structures. */ static void opd_delete_proc(struct opd_proc * proc) { --nr_procs; list_del(&proc->next); opd_kill_maps(proc); if (proc->name) free((char *)proc->name); free(proc); } void opd_proc_cleanup(void) { opd_for_each_proc(opd_delete_proc); } /** * opd_age_proc - age a struct opd_proc * @param proc proc to age * * age dead proc in such way if a proc doesn't receive any samples * between two age_proc the opd_proc struct is deleted */ static void opd_age_proc(struct opd_proc * proc) { // delay death whilst its still being accessed if (proc->dead) { proc->dead += proc->accessed; proc->accessed = 0; if (--proc->dead == 0) opd_delete_proc(proc); } } void opd_age_procs(void) { opd_for_each_proc(opd_age_proc); } /** * opd_remove_kernel_mapping - remove all kernel mapping for an opd_proc * @param proc proc where mappings must be updated. * * invalidate (by removing them) all kernel mapping. This function do nothing * when separate_kernel == 0 because we don't add mapping for kernel * sample in proc struct. */ static void opd_remove_kernel_mapping(struct opd_proc * proc) { struct list_head * pos, * pos2; list_for_each_safe(pos, pos2, &proc->maps) { struct opd_map * map = list_entry(pos, struct opd_map, next); if (opd_eip_is_kernel(map->start + map->offset)) { list_del(pos); opd_delete_image(map->image); free(map); } } } void opd_clear_kernel_mapping(void) { opd_for_each_proc(opd_remove_kernel_mapping); }