[e3b412]: daemon / liblegacy / opd_proc.c Maximize Restore History

Download this file

opd_proc.c    433 lines (356 with data), 10.5 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/**
* @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_insert(&sfile->sample_file, offset, 1);
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);
}