operf_sfile.cpp

Go to the documentation of this file.
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         /* fname_ptr will point at the first character in the binary file's name
00058          * which we'll use for hashing.  We don't need to hash on the whole
00059          * pathname to get a decent hash.  Arbitrarily, we'll hash on the
00060          * last 16 chars (or last fname_len chars if fname_len < 16).
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         // Now do the same for image name
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     /* this is a simplified check for "is a kernel image" AND
00088      * "is the right kernel image". Also handles no-vmlinux
00089      * correctly.
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     // TODO:  handle extended
00158     /*
00159     if (trans->ext)
00160         opd_ext_operf_sfile_create(sf);
00161     else
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     // TODO: handle extended
00225     //opd_ext_operf_sfile_dup(to, from);
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     // TODO: handle extended
00244     /*
00245     if ((trans->ext) != NULL)
00246         return opd_ext_operf_sfile_get(trans, is_cg);
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     /* Need to look for the right 'to'. Since we're looking for
00263      * 'last', we use its hash.
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     /* Error is logged by opd_open_sample_file */
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     /* absolute value -> offset */
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     /* Possible narrowings to 32-bit value only. */
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     /* absolute value -> offset */
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     /* it's OK to close a non-open odb file */
00413     for (i = 0; i < op_nr_counters; ++i)
00414         odb_close(&sf->files[i]);
00415 
00416     // TODO: handle extended
00417     //opd_ext_operf_sfile_close(sf);
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     // TODO: handle extended
00439     //opd_ext_operf_sfile_sync(sf);
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  * Clear out older sfiles. Note the current sfiles we're using
00524  * will not be present in this list, due to operf_sfile_get/put() pairs
00525  * around the caller of this.
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 }

Generated on 8 Nov 2012 for Oprofile by  doxygen 1.6.1