blob: 8a2d4e8fb3518488f2729e1281e59f576b28d528 [file] [log] [blame]
/**
* @file opd_parse_proc.c
* Parsing of /proc/#pid
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon
* @author Philippe Elie
*/
#include "op_libiberty.h"
#include "opd_parse_proc.h"
#include "opd_proc.h"
#include "opd_mapping.h"
#include "opd_image.h"
#include "opd_printf.h"
#include "op_file.h"
#include "op_fileio.h"
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* opd_add_ascii_map - parse an ASCII map string for a process
* @param proc process to add map to
* @param line 0-terminated ASCII string
* @param image_name the binary application name
*
* Attempt to parse the string @line for map information
* and add the info to the process @proc. Returns %1
* on success, %0 otherwise.
*
* The parsing is based on Linux 2.4 format, which looks like this :
*
* 4001e000-400fc000 r-xp 00000000 03:04 31011 /lib/libc-2.1.2.so
*/
/* FIXME: handle (deleted) */
static int opd_add_ascii_map(struct opd_proc * proc, char const * line,
char * const image_name)
{
unsigned long offset, start, end;
struct opd_image * image;
char const * cp = line;
/* skip to protection field */
while (*cp && *cp != ' ')
cp++;
/* handle rwx */
if (!*cp || (!*(++cp)) || (!*(++cp)) || (*(++cp) != 'x'))
return 0;
/* get start and end from "40000000-4001f000" */
if (sscanf(line, "%lx-%lx", &start, &end) != 2)
return 0;
/* "p " */
cp += 2;
/* read offset */
if (sscanf(cp, "%lx", &offset) != 1)
return 0;
while (*cp && *cp != '/')
cp++;
if (!*cp)
return 0;
image = opd_get_image(cp, image_name, 0, proc->tid, proc->tgid);
if (!image)
return 0;
opd_add_mapping(proc, image, start, offset, end);
return 1;
}
/**
* opd_get_ascii_maps - read all maps for a process
* @param proc process to work on
*
* Read the /proc/<pid>/maps file and add all
* mapping information found to the process @proc.
*/
static void opd_get_ascii_maps(struct opd_proc * proc)
{
FILE * fp;
char mapsfile[20] = "/proc/";
char * line;
char exe_name[20];
char * image_name;
struct list_head * pos;
snprintf(mapsfile + 6, 6, "%hu", proc->tid);
strcpy(exe_name, mapsfile);
strcat(mapsfile, "/maps");
fp = op_try_open_file(mapsfile, "r");
if (!fp)
return;
strcat(exe_name, "/exe");
image_name = xmalloc(PATH_MAX);
if (!realpath(exe_name, image_name))
/* kernel thread are invalid symlink */
strcpy(image_name, exe_name);
verbprintf(vmisc, "image name %s for pid %u %u\n", image_name, proc->tid, proc->tgid);
while (1) {
line = op_get_line(fp);
if (!line)
break;
opd_add_ascii_map(proc, line, image_name);
free(line);
}
/* dae assume than the first map added is the primary image name, this
* is always true at exec time but not for /proc/pid so restore
* the primary image name
*/
list_for_each(pos, &proc->maps) {
struct opd_map * map = list_entry(pos, struct opd_map, next);
if (!strcmp(map->image->name, image_name)) {
if (pos != proc->maps.next) {
fprintf(stderr, "swap map for image %s from %s to %s\n", image_name, proc->name, map->image->name);
free((char *)proc->name);
proc->name = xstrdup(map->image->name);
}
break;
}
}
if (list_empty(&proc->maps)) {
/* we always need a valid proc->maps[0], we artificially give
* a map of length zero so on no samples will never go to this
* map. This is used only with --separate-samples and kernel
* thread when adding vmlinux and module maps to proc->maps[]
*/
/* FIXME: use the first field of /proc/pid/status as proc name
* for now we use /proc/%pid/exe as name */
struct opd_image * image = opd_get_image(image_name,
image_name, 0, proc->tid, proc->tgid);
if (image)
opd_add_mapping(proc, image, 0, 0, 0);
}
if (image_name)
free(image_name);
op_close_file(fp);
}
static u32 read_tgid(u32 tid)
{
char status_file[30] = "/proc/";
char * line;
FILE * fp;
u32 tgid;
snprintf(status_file + 6, 6, "%hu", tid);
strcat(status_file, "/status");
fp = op_try_open_file(status_file, "r");
if (!fp)
return 0;
while (1) {
line = op_get_line(fp);
if (!line)
break;
if (sscanf(line, "Tgid: %u", &tgid) == 1) {
free(line);
op_close_file(fp);
return tgid;
}
free(line);
}
op_close_file(fp);
return 0;
}
void opd_get_ascii_procs(void)
{
DIR * dir;
struct dirent * dirent;
struct opd_proc * proc;
u32 pid;
if (!(dir = opendir("/proc"))) {
perror("oprofiled: /proc directory could not be opened. ");
exit(EXIT_FAILURE);
}
while ((dirent = readdir(dir))) {
if (sscanf(dirent->d_name, "%u", &pid) == 1) {
u32 tgid = read_tgid(pid);
verbprintf(vmisc, "ASCII added %u %u\n", pid, tgid);
proc = opd_get_proc(pid, tgid);
if (!proc)
proc = opd_new_proc(pid, tgid);
opd_get_ascii_maps(proc);
}
}
closedir(dir);
}