00001
00013 #include <stdio.h>
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include <iostream>
00017
00018 #include "operf_sfile.h"
00019 #include "operf_kernel.h"
00020 #include "operf_utils.h"
00021 #include "cverb.h"
00022 #include "op_string.h"
00023 #include "operf_mangling.h"
00024 #include "operf_stats.h"
00025 #include "op_libiberty.h"
00026
00027 #define HASH_SIZE 2048
00028 #define HASH_BITS (HASH_SIZE - 1)
00029
00031 static struct list_head hashes[HASH_SIZE];
00032
00034 static LIST_HEAD(lru_list);
00035
00036
00037 static unsigned long
00038 sfile_hash(struct operf_transient const * trans, struct operf_kernel_image * ki)
00039 {
00040 unsigned long val = 0;
00041
00042 val ^= trans->tgid << 2;
00043
00044 if (operf_options::separate_cpu)
00045 val ^= trans->cpu;
00046
00047 if (trans->in_kernel) {
00048 val ^= ki->start >> 14;
00049 val ^= ki->end >> 7;
00050 }
00051
00052 if (trans->is_anon) {
00053 val ^= trans->start_addr >> VMA_SHIFT;
00054 val ^= trans->end_addr >> (VMA_SHIFT + 1);
00055 } else {
00056 size_t fname_len = trans->app_len;
00057
00058
00059
00060
00061
00062 unsigned int fname_hash_len = (fname_len < 16) ? fname_len : 16;
00063 const char * fname_ptr = trans->app_filename + fname_len - fname_hash_len;
00064 for (unsigned int i = 0; i < fname_hash_len; i++)
00065 val = ((val << 5) + val) ^ fname_ptr[i];
00066
00067
00068 fname_len = trans->image_len;
00069 fname_hash_len = (fname_len < 16) ? fname_len : 16;
00070 fname_ptr = trans->image_name + fname_len - fname_hash_len;
00071 for (unsigned int i = 0; i < fname_hash_len; i++)
00072 val = ((val << 5) + val) ^ fname_ptr[i];
00073 }
00074
00075 return val & HASH_BITS;
00076 }
00077
00078
00079 static int
00080 do_match(struct operf_sfile const * sf, struct operf_kernel_image const * ki,
00081 bool is_anon, const char * image_name, size_t image_len,
00082 const char * appname, size_t app_len,
00083 pid_t tgid, pid_t tid, unsigned int cpu)
00084 {
00085 size_t shortest_image_len, shortest_app_len;
00086
00087
00088
00089
00090
00091 if (sf->kernel != ki)
00092 return 0;
00093
00094 if (sf->tid != tid || sf->tgid != tgid)
00095 return 0;
00096
00097 if (sf->is_anon != is_anon)
00098 return 0;
00099
00100 if ((sf->app_len != app_len) || (sf->image_len != image_len))
00101 return 0;
00102
00103 if (operf_options::separate_cpu) {
00104 if (sf->cpu != cpu)
00105 return 0;
00106 }
00107
00108 if (ki)
00109 return 1;
00110
00111 shortest_image_len = sf->image_len < image_len ? sf->image_len : image_len;
00112 if (strncmp(sf->image_name, image_name, shortest_image_len))
00113 return 0;
00114
00115 shortest_app_len = sf->app_len < app_len ? sf->app_len : app_len;
00116 return !strncmp(sf->app_filename, appname, shortest_app_len);
00117
00118 }
00119
00120 int
00121 operf_sfile_equal(struct operf_sfile const * sf, struct operf_sfile const * sf2)
00122 {
00123 return do_match(sf, sf2->kernel,
00124 sf2->is_anon,
00125 sf2->image_name, sf2->image_len,
00126 sf2->app_filename, sf2->app_len,
00127 sf2->tgid, sf2->tid, sf2->cpu);
00128 }
00129
00130
00132 static struct operf_sfile *
00133 create_sfile(unsigned long hash, struct operf_transient const * trans,
00134 struct operf_kernel_image * ki)
00135 {
00136 size_t i;
00137 struct operf_sfile * sf;
00138
00139 sf = (operf_sfile *)xmalloc(sizeof(struct operf_sfile));
00140
00141 sf->hashval = hash;
00142 sf->tid = trans->tid;
00143 sf->tgid = trans->tgid;
00144 sf->cpu = 0;
00145 sf->kernel = ki;
00146 sf->image_name = trans->image_name;
00147 sf->app_filename = trans->app_filename;
00148 sf->image_len = trans->image_len;
00149 sf->app_len = trans->app_len;
00150 sf->is_anon = trans->is_anon;
00151 sf->start_addr = trans->start_addr;
00152 sf->end_addr = trans->end_addr;
00153
00154 for (i = 0 ; i < op_nr_counters ; ++i)
00155 odb_init(&sf->files[i]);
00156
00157
00158
00159
00160
00161
00162
00163 sf->ext_files = NULL;
00164
00165 for (i = 0; i < CG_HASH_SIZE; ++i)
00166 list_init(&sf->cg_hash[i]);
00167
00168 if (operf_options::separate_cpu)
00169 sf->cpu = trans->cpu;
00170
00171 return sf;
00172 }
00173 #include <iostream>
00174 using namespace std;
00175 struct operf_sfile * operf_sfile_find(struct operf_transient const * trans)
00176 {
00177 struct operf_sfile * sf;
00178 struct list_head * pos;
00179 struct operf_kernel_image * ki = NULL;
00180 unsigned long hash;
00181
00182
00183 if (trans->in_kernel) {
00184 ki = operf_find_kernel_image(trans->pc);
00185 if (!ki) {
00186 if (cverb << vsfile)
00187 cout << "Lost kernel sample " << std::hex << trans->pc << std::endl;;
00188 operf_stats[OPERF_LOST_KERNEL]++;
00189 return NULL;
00190 }
00191 }
00192
00193 hash = sfile_hash(trans, ki);
00194 list_for_each(pos, &hashes[hash]) {
00195 sf = list_entry(pos, struct operf_sfile, hash);
00196 if (do_match(sf, ki,
00197 trans->is_anon,
00198 trans->image_name, trans->image_len,
00199 trans->app_filename, trans->app_len,
00200 trans->tgid, trans->tid, trans->cpu)) {
00201 operf_sfile_get(sf);
00202 goto lru;
00203 }
00204 }
00205 sf = create_sfile(hash, trans, ki);
00206 list_add(&sf->hash, &hashes[hash]);
00207
00208
00209 lru:
00210 operf_sfile_put(sf);
00211 return sf;
00212 }
00213
00214
00215 void operf_sfile_dup(struct operf_sfile * to, struct operf_sfile * from)
00216 {
00217 size_t i;
00218
00219 memcpy(to, from, sizeof (struct operf_sfile));
00220
00221 for (i = 0 ; i < op_nr_counters ; ++i)
00222 odb_init(&to->files[i]);
00223
00224
00225
00226
00227 for (i = 0; i < CG_HASH_SIZE; ++i)
00228 list_init(&to->cg_hash[i]);
00229
00230 list_init(&to->hash);
00231 list_init(&to->lru);
00232 }
00233
00234 static odb_t * get_file(struct operf_transient const * trans, int is_cg)
00235 {
00236 struct operf_sfile * sf = trans->current;
00237 struct operf_sfile * last = trans->last;
00238 struct operf_cg_entry * cg;
00239 struct list_head * pos;
00240 unsigned long hash;
00241 odb_t * file;
00242
00243
00244
00245
00246
00247
00248
00249 if (trans->event >= (int)op_nr_counters) {
00250 fprintf(stderr, "%s: Invalid counter %d\n", __FUNCTION__,
00251 trans->event);
00252 abort();
00253 }
00254
00255 file = &sf->files[trans->event];
00256
00257 if (!is_cg)
00258 goto open;
00259
00260 hash = last->hashval & (CG_HASH_SIZE - 1);
00261
00262
00263
00264
00265 list_for_each(pos, &sf->cg_hash[hash]) {
00266 cg = list_entry(pos, struct operf_cg_entry, hash);
00267 if (operf_sfile_equal(last, &cg->to)) {
00268 file = &cg->to.files[trans->event];
00269 goto open;
00270 }
00271 }
00272
00273 cg = (operf_cg_entry *)xmalloc(sizeof(struct operf_cg_entry));
00274 operf_sfile_dup(&cg->to, last);
00275 list_add(&cg->hash, &sf->cg_hash[hash]);
00276 file = &cg->to.files[trans->event];
00277
00278 open:
00279 if (!odb_open_count(file))
00280 operf_open_sample_file(file, last, sf, trans->event, is_cg);
00281
00282
00283 if (!odb_open_count(file))
00284 return NULL;
00285
00286 return file;
00287 }
00288
00289
00290 static void verbose_print_sample(struct operf_sfile * sf, vma_t pc, uint counter)
00291 {
00292 printf("0x%llx(%u): ", pc, counter);
00293 if (sf->is_anon) {
00294 printf("anon (tgid %u, 0x%llx-0x%llx), ",
00295 (unsigned int)sf->tgid,
00296 sf->start_addr, sf->end_addr);
00297 } else if (sf->kernel) {
00298 printf("kern (name %s, 0x%llx-0x%llx), ", sf->kernel->name,
00299 sf->kernel->start, sf->kernel->end);
00300 } else {
00301 printf("%s), ", sf->image_name);
00302 }
00303 printf("app: %s: ", sf->app_filename);
00304 }
00305
00306
00307 static void verbose_sample(struct operf_transient const * trans, vma_t pc)
00308 {
00309 printf("Sample ");
00310 verbose_print_sample(trans->current, pc, trans->event);
00311 printf("\n");
00312 }
00313
00314 static void
00315 verbose_arc(struct operf_transient const * trans, vma_t from, vma_t to)
00316 {
00317 printf("Arc ");
00318 verbose_print_sample(trans->current, from, trans->event);
00319 printf(" -> 0x%llx", to);
00320 printf("\n");
00321 }
00322
00323 void operf_sfile_log_arc(struct operf_transient const * trans)
00324 {
00325 int err;
00326 vma_t from = trans->pc;
00327 vma_t to = trans->last_pc;
00328 uint64_t key;
00329 odb_t * file;
00330
00331 file = get_file(trans, 1);
00332
00333
00334 if (trans->current->kernel)
00335 from -= trans->current->kernel->start;
00336
00337 if (trans->last->kernel)
00338 to -= trans->last->kernel->start;
00339
00340 if (trans->current->is_anon)
00341 from -= trans->current->start_addr;
00342
00343 if (trans->last->is_anon)
00344 to -= trans->last->start_addr;
00345
00346 if (cverb << varcs)
00347 verbose_arc(trans, from, to);
00348
00349 if (!file) {
00350 operf_stats[OPERF_LOST_SAMPLEFILE]++;
00351 return;
00352 }
00353
00354
00355 key = to & (0xffffffff);
00356 key |= ((uint64_t)from) << 32;
00357
00358 err = odb_update_node(file, key);
00359 if (err) {
00360 fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
00361 abort();
00362 }
00363
00364 }
00365
00366 void operf_sfile_log_sample(struct operf_transient const * trans)
00367 {
00368 operf_sfile_log_sample_count(trans, 1);
00369 }
00370
00371
00372 void operf_sfile_log_sample_count(struct operf_transient const * trans,
00373 unsigned long int count)
00374 {
00375 int err;
00376 vma_t pc = trans->pc;
00377 odb_t * file;
00378
00379 file = get_file(trans, 0);
00380
00381
00382 if (trans->current->kernel)
00383 pc -= trans->current->kernel->start;
00384 if (trans->current->is_anon)
00385 pc -= trans->current->start_addr;
00386
00387 if (cverb << vsfile)
00388 verbose_sample(trans, pc);
00389 if (!file) {
00390 operf_stats[OPERF_LOST_SAMPLEFILE]++;
00391 return;
00392 }
00393 err = odb_update_node_with_offset(file,
00394 (odb_key_t)pc,
00395 count);
00396 if (err) {
00397 fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
00398 abort();
00399 }
00400 operf_stats[OPERF_SAMPLES]++;
00401 if (trans->in_kernel)
00402 operf_stats[OPERF_KERNEL]++;
00403 else
00404 operf_stats[OPERF_PROCESS]++;
00405 }
00406
00407
00408 static int close_sfile(struct operf_sfile * sf, void * data __attribute__((unused)))
00409 {
00410 size_t i;
00411
00412
00413 for (i = 0; i < op_nr_counters; ++i)
00414 odb_close(&sf->files[i]);
00415
00416
00417
00418
00419 return 0;
00420 }
00421
00422
00423 static void kill_sfile(struct operf_sfile * sf)
00424 {
00425 close_sfile(sf, NULL);
00426 list_del(&sf->hash);
00427 list_del(&sf->lru);
00428 }
00429
00430
00431 static int sync_sfile(struct operf_sfile * sf, void * data __attribute__((unused)))
00432 {
00433 size_t i;
00434
00435 for (i = 0; i < op_nr_counters; ++i)
00436 odb_sync(&sf->files[i]);
00437
00438
00439
00440
00441 return 0;
00442 }
00443
00444
00445 static int is_sfile_kernel(struct operf_sfile * sf, void * data __attribute__((unused)))
00446 {
00447 return !!sf->kernel;
00448 }
00449
00450
00451 typedef int (*operf_sfile_func)(struct operf_sfile *, void *);
00452
00453 static void
00454 for_one_sfile(struct operf_sfile * sf, operf_sfile_func func, void * data)
00455 {
00456 size_t i;
00457 int free_sf = func(sf, data);
00458
00459 for (i = 0; i < CG_HASH_SIZE; ++i) {
00460 struct list_head * pos;
00461 struct list_head * pos2;
00462 list_for_each_safe(pos, pos2, &sf->cg_hash[i]) {
00463 struct operf_cg_entry * cg =
00464 list_entry(pos, struct operf_cg_entry, hash);
00465 if (free_sf || func(&cg->to, data)) {
00466 kill_sfile(&cg->to);
00467 list_del(&cg->hash);
00468 free(cg);
00469 }
00470 }
00471 }
00472
00473 if (free_sf) {
00474 kill_sfile(sf);
00475 free(sf);
00476 }
00477 }
00478
00479
00480 static void for_each_sfile(operf_sfile_func func, void * data)
00481 {
00482 struct list_head * pos;
00483 struct list_head * pos2;
00484
00485 list_for_each_safe(pos, pos2, &lru_list) {
00486 struct operf_sfile * sf = list_entry(pos, struct operf_sfile, lru);
00487 for_one_sfile(sf, func, data);
00488 }
00489 }
00490
00491
00492 void operf_sfile_clear_kernel(void)
00493 {
00494 for_each_sfile(is_sfile_kernel, NULL);
00495 }
00496
00497
00498 void operf_sfile_sync_files(void)
00499 {
00500 for_each_sfile(sync_sfile, NULL);
00501 }
00502
00503 static int _release_resources(struct operf_sfile *sf __attribute__((unused)), void * p __attribute__((unused)))
00504 {
00505 return 1;
00506 }
00507
00508 void operf_sfile_close_files(void)
00509 {
00510 for_each_sfile(_release_resources, NULL);
00511 }
00512
00513
00514 static int always_true(void)
00515 {
00516 return 1;
00517 }
00518
00519
00520 #define LRU_AMOUNT 256
00521
00522
00523
00524
00525
00526
00527 int operf_sfile_lru_clear(void)
00528 {
00529 struct list_head * pos;
00530 struct list_head * pos2;
00531 int amount = LRU_AMOUNT;
00532
00533 if (list_empty(&lru_list))
00534 return 1;
00535
00536 list_for_each_safe(pos, pos2, &lru_list) {
00537 struct operf_sfile * sf;
00538 if (!--amount)
00539 break;
00540 sf = list_entry(pos, struct operf_sfile, lru);
00541 for_one_sfile(sf, (operf_sfile_func)always_true, NULL);
00542 }
00543
00544 return 0;
00545 }
00546
00547
00548 void operf_sfile_get(struct operf_sfile * sf)
00549 {
00550 if (sf)
00551 list_del(&sf->lru);
00552 }
00553
00554
00555 void operf_sfile_put(struct operf_sfile * sf)
00556 {
00557 if (sf)
00558 list_add_tail(&sf->lru, &lru_list);
00559 }
00560
00561
00562 void operf_sfile_init(void)
00563 {
00564 size_t i = 0;
00565
00566 for (; i < HASH_SIZE; ++i)
00567 list_init(&hashes[i]);
00568 }