opd_sfile.c

Go to the documentation of this file.
00001 
00012 #include "opd_sfile.h"
00013 
00014 #include "opd_trans.h"
00015 #include "opd_kernel.h"
00016 #include "opd_mangling.h"
00017 #include "opd_anon.h"
00018 #include "opd_printf.h"
00019 #include "opd_stats.h"
00020 #include "opd_extended.h"
00021 #include "oprofiled.h"
00022 
00023 #include "op_libiberty.h"
00024 
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <unistd.h>
00029 #include <sys/stat.h>
00030 #include <fcntl.h>
00031 
00032 #define HASH_SIZE 2048
00033 #define HASH_BITS (HASH_SIZE - 1)
00034 
00036 static struct list_head hashes[HASH_SIZE];
00037 
00038 /* This data structure is used to help us determine when we should
00039  * discard user context kernel samples for which we no longer have
00040  * an app name to which we can attribute them.  This can happen (especially
00041  * on a busy system) in the following scenario:
00042  *  - A user context switch occurs.
00043  *  - User context kernel samples are recorded for this process.
00044  *  - The user process ends.
00045  *  - The above-mentioned sample information is first recorded into the per-CPU
00046  *  buffer and later transferred to the main event buffer.  Since the process
00047  *  for which this context switch was recorded ended before the transfer
00048  *  occurs, the app cookie that is recorded into the event buffer along with the
00049  *  CTX_SWITCH_CODE will be set to NO_COOKIE. When the oprofile userspace daemon
00050  *  processes the CTX_SWITCH_CODE, it sets trans->app_cookie to NO_COOKIE and then
00051  *  continues to process the kernel samples. But having no appname in order to
00052  *  locate the appropriate sample file, it creates a new sample file of the form:
00053  *  <session_dir>/current/{kern}/<some_kernel_image>/{dep}/{kern}/<some_kernel_image>/<event_spec>.<tgid>.<tid>.<cpu>
00054  *
00055  *  This is not really an invalid form for sample files, since it is certainly valid for
00056  *  oprofile to collect samples for kernel threads that are not running in any process context.
00057  *  Such samples would be stored in sample files like this, and opreport would show those
00058  *  samples as having an appname of "<some_kernel_image>".  But if the tgid/tid info for
00059  *  the sample is from a defunct user process, we should discard these samples.  Not doing so
00060  *  can lead to strange results when generating reports by tgid/tid (i.e., appname of
00061  *  "<some_kernel_image>" instead of the real app name associated with the given tgid/tid.
00062  *  The following paragraph describes the technique for identifying and discarding such samples.
00063  *
00064  * When processing a kernel sample for which trans->app_cookie==NO_COOKIE, we inspect the
00065  * /proc/<pid>/cmdline file.  Housekeeping types of kernel threads (e.g., kswapd, watchdog)
00066  * won't have a command line since they exist and operate outside of a process context.
00067  * However, other kernel "tasks" do operate within a process context (e.g., some kernel
00068  * driver functions, kernel functions invoked via a syscall, etc.).  When we get samples
00069  * for the latter type of task but no longer have app name info for the process for which
00070  * the kernel task is performing work, we cannot correctly attribute those kernel samples
00071  * to a user application, so they should be discarded.  We classify the two different types
00072  * of kernel "tasks" based on whether or not the /proc/<pid>/cmdline is empty.  We cache
00073  * the results in kernel_cmdlines for fast lookup when processing samples.
00074  */
00075 static struct list_head kernel_cmdlines[HASH_SIZE];
00076 struct kern_cmdline {
00077     pid_t kern_pid;
00078     struct list_head hash;
00079     unsigned int has_cmdline;
00080 };
00081 
00083 static LIST_HEAD(lru_list);
00084 
00085 
00086 /* FIXME: can undoubtedly improve this hashing */
00088 static unsigned long
00089 sfile_hash(struct transient const * trans, struct kernel_image * ki)
00090 {
00091     unsigned long val = 0;
00092     
00093     if (separate_thread) {
00094         val ^= trans->tid << 2;
00095         val ^= trans->tgid << 2;
00096     }
00097 
00098     if (separate_kernel || ((trans->anon || separate_lib) && !ki))
00099         val ^= trans->app_cookie >> (DCOOKIE_SHIFT + 3);
00100 
00101     if (separate_cpu)
00102         val ^= trans->cpu;
00103 
00104     /* cookie meaningless for kernel, shouldn't hash */
00105     if (trans->in_kernel) {
00106         val ^= ki->start >> 14;
00107         val ^= ki->end >> 7;
00108         return val & HASH_BITS;
00109     }
00110 
00111     if (trans->cookie != NO_COOKIE) {
00112         val ^= trans->cookie >> DCOOKIE_SHIFT;
00113         return val & HASH_BITS;
00114     }
00115 
00116     if (!separate_thread)
00117         val ^= trans->tgid << 2;
00118 
00119     if (trans->anon) {
00120         val ^= trans->anon->start >> VMA_SHIFT;
00121         val ^= trans->anon->end >> (VMA_SHIFT + 1);
00122     }
00123 
00124     return val & HASH_BITS;
00125 }
00126 
00127 
00128 static int
00129 do_match(struct sfile const * sf, cookie_t cookie, cookie_t app_cookie,
00130          struct kernel_image const * ki, struct anon_mapping const * anon,
00131          pid_t tgid, pid_t tid, unsigned int cpu)
00132 {
00133     /* this is a simplified check for "is a kernel image" AND
00134      * "is the right kernel image". Also handles no-vmlinux
00135      * correctly.
00136      */
00137     if (sf->kernel != ki)
00138         return 0;
00139 
00140     if (separate_thread) {
00141         if (sf->tid != tid || sf->tgid != tgid)
00142             return 0;
00143     }
00144 
00145     if (separate_cpu) {
00146         if (sf->cpu != cpu)
00147             return 0;
00148     }
00149 
00150     if (separate_kernel || ((anon || separate_lib) && !ki)) {
00151         if (sf->app_cookie != app_cookie)
00152             return 0;
00153     }
00154 
00155     /* ignore the cached trans->cookie for kernel images,
00156      * it's meaningless and we checked all others already
00157      */
00158     if (ki)
00159         return 1;
00160 
00161     if (sf->anon != anon)
00162         return 0;
00163 
00164     return sf->cookie == cookie;
00165 }
00166 
00167 
00168 static int
00169 trans_match(struct transient const * trans, struct sfile const * sfile,
00170             struct kernel_image const * ki)
00171 {
00172     return do_match(sfile, trans->cookie, trans->app_cookie, ki,
00173                     trans->anon, trans->tgid, trans->tid, trans->cpu);
00174 }
00175 
00176 
00177 int
00178 sfile_equal(struct sfile const * sf, struct sfile const * sf2)
00179 {
00180     return do_match(sf, sf2->cookie, sf2->app_cookie, sf2->kernel,
00181                     sf2->anon, sf2->tgid, sf2->tid, sf2->cpu);
00182 }
00183 
00184 
00185 static int
00186 is_sf_ignored(struct sfile const * sf)
00187 {
00188     if (sf->kernel) {
00189         if (!is_image_ignored(sf->kernel->name))
00190             return 0;
00191 
00192         /* Let a dependent kernel image redeem the sf if we're
00193          * executing on behalf of an application.
00194          */
00195         return is_cookie_ignored(sf->app_cookie);
00196     }
00197 
00198     /* Anon regions are always dependent on the application.
00199      * Otherwise, let a dependent image redeem the sf.
00200      */
00201     if (sf->anon || is_cookie_ignored(sf->cookie))
00202         return is_cookie_ignored(sf->app_cookie);
00203 
00204     return 0;
00205 }
00206 
00207 
00209 static struct sfile *
00210 create_sfile(unsigned long hash, struct transient const * trans,
00211              struct kernel_image * ki)
00212 {
00213     size_t i;
00214     struct sfile * sf;
00215 
00216     sf = xmalloc(sizeof(struct sfile));
00217 
00218     sf->hashval = hash;
00219 
00220     /* The logic here: if we're in the kernel, the cached cookie is
00221      * meaningless (though not the app_cookie if separate_kernel)
00222      */
00223     sf->cookie = trans->in_kernel ? INVALID_COOKIE : trans->cookie;
00224     sf->app_cookie = INVALID_COOKIE;
00225     sf->tid = (pid_t)-1;
00226     sf->tgid = (pid_t)-1;
00227     sf->cpu = 0;
00228     sf->kernel = ki;
00229     sf->anon = trans->anon;
00230 
00231     for (i = 0 ; i < op_nr_counters ; ++i)
00232         odb_init(&sf->files[i]);
00233 
00234     if (trans->ext)
00235         opd_ext_sfile_create(sf);
00236     else
00237         sf->ext_files = NULL;
00238 
00239     for (i = 0; i < CG_HASH_SIZE; ++i)
00240         list_init(&sf->cg_hash[i]);
00241 
00242     if (separate_thread)
00243         sf->tid = trans->tid;
00244     if (separate_thread || trans->cookie == NO_COOKIE)
00245         sf->tgid = trans->tgid;
00246 
00247     if (separate_cpu)
00248         sf->cpu = trans->cpu;
00249 
00250     if (separate_kernel || ((trans->anon || separate_lib) && !ki))
00251         sf->app_cookie = trans->app_cookie;
00252 
00253     sf->ignored = is_sf_ignored(sf);
00254 
00255     sf->embedded_offset = trans->embedded_offset;
00256 
00257     /* If embedded_offset is a valid value, it means we're
00258      * processing a Cell BE SPU profile; in which case, we
00259      * want sf->app_cookie to hold trans->app_cookie.
00260      */
00261     if (trans->embedded_offset != UNUSED_EMBEDDED_OFFSET)
00262         sf->app_cookie = trans->app_cookie;
00263     return sf;
00264 }
00265 
00266 
00267 struct sfile * sfile_find(struct transient const * trans)
00268 {
00269     struct sfile * sf;
00270     struct list_head * pos;
00271     struct kernel_image * ki = NULL;
00272     unsigned long hash;
00273 
00274     /* There is a small race where this *can* happen, see
00275      * caller of cpu_buffer_reset() in the kernel
00276      */
00277     if (trans->in_kernel == -1) {
00278         verbprintf(vsamples, "Losing sample at 0x%llx of unknown provenance.\n",
00279                    trans->pc);
00280         opd_stats[OPD_NO_CTX]++;
00281         return NULL;
00282     }
00283 
00284     /* we might need a kernel image start/end to hash on */
00285     if (trans->in_kernel) {
00286         ki = find_kernel_image(trans);
00287         if (!ki) {
00288             verbprintf(vsamples, "Lost kernel sample %llx\n", trans->pc);
00289             opd_stats[OPD_LOST_KERNEL]++;
00290             return NULL;
00291         }
00292         // We *know* that PID 0, 1, and 2 are pure kernel context tasks, so
00293         // we always want to keep these samples.
00294         if ((trans->tgid == 0) || (trans->tgid == 1) || (trans->tgid == 2))
00295             goto find_sfile;
00296 
00297         // Decide whether or not this kernel sample should be discarded.
00298         // See detailed description above where the kernel_cmdlines hash
00299         // table is defined.
00300         if (trans->app_cookie == NO_COOKIE) {
00301             int found = 0;
00302             struct kern_cmdline * kcmd;
00303             hash = (trans->tgid << 2) & HASH_BITS;
00304             list_for_each(pos, &kernel_cmdlines[hash]) {
00305                 kcmd = list_entry(pos, struct kern_cmdline, hash);
00306                 if (kcmd->kern_pid == trans->tgid) {
00307                     found = 1;
00308                     if (kcmd->has_cmdline) {
00309                         verbprintf(vsamples,
00310                                    "Dropping user context kernel sample 0x%llx "
00311                                    "for process %u due to no app cookie available.\n",
00312                                    (unsigned long long)trans->pc, trans->tgid);
00313                         opd_stats[OPD_NO_APP_KERNEL_SAMPLE]++;
00314                         return NULL;
00315                     }
00316                     break;
00317                 }
00318             }
00319             if (!found) {
00320                 char name[32], dst[8];
00321                 int fd, dropped = 0;
00322                 kcmd = (struct kern_cmdline *)xmalloc(sizeof(*kcmd));
00323                 kcmd->kern_pid = trans->tgid;
00324                 snprintf(name, sizeof name, "/proc/%u/cmdline", trans->tgid);
00325                 fd = open(name, O_RDONLY);
00326                 if(fd==-1) {
00327                     // Most likely due to process ending, so we'll assume it used to have a cmdline
00328                     kcmd->has_cmdline = 1;
00329                     verbprintf(vsamples,
00330                                "Open of /proc/%u/cmdline failed, so dropping "
00331                                "kernel sameple 0x%llx\n",
00332                                trans->tgid, (unsigned long long)trans->pc);
00333                     opd_stats[OPD_NO_APP_KERNEL_SAMPLE]++;
00334                     dropped = 1;
00335                 } else {
00336                     if((read(fd, dst, 8) < 1)) {
00337                         verbprintf(vsamples, "No cmdline for PID %u\n", trans->tgid);
00338                         kcmd->has_cmdline = 0;
00339                     } else {
00340                         // This *really* shouldn't happen.  If it does, then why don't
00341                         // we have an app_cookie?
00342                         dst[7] = '\0';
00343                         verbprintf(vsamples, "Start of cmdline for PID %u is %s\n", trans->tgid, dst);
00344                         kcmd->has_cmdline = 1;
00345                         opd_stats[OPD_NO_APP_KERNEL_SAMPLE]++;
00346                         dropped = 1;
00347                     }
00348                 }
00349                 list_add(&kcmd->hash, &kernel_cmdlines[hash]);
00350                 if (dropped)
00351                     return NULL;
00352             }
00353         }
00354     } else if (trans->cookie == NO_COOKIE && !trans->anon) {
00355         if (vsamples) {
00356             char const * app = verbose_cookie(trans->app_cookie);
00357             printf("No anon map for pc %llx, app %s.\n",
00358                    trans->pc, app);
00359         }
00360         opd_stats[OPD_LOST_NO_MAPPING]++;
00361         return NULL;
00362     }
00363 
00364 find_sfile:
00365     hash = sfile_hash(trans, ki);
00366     list_for_each(pos, &hashes[hash]) {
00367         sf = list_entry(pos, struct sfile, hash);
00368         if (trans_match(trans, sf, ki)) {
00369             sfile_get(sf);
00370             goto lru;
00371         }
00372     }
00373 
00374     sf = create_sfile(hash, trans, ki);
00375     list_add(&sf->hash, &hashes[hash]);
00376 
00377 lru:
00378     sfile_put(sf);
00379     return sf;
00380 }
00381 
00382 
00383 void sfile_dup(struct sfile * to, struct sfile * from)
00384 {
00385     size_t i;
00386 
00387     memcpy(to, from, sizeof (struct sfile));
00388 
00389     for (i = 0 ; i < op_nr_counters ; ++i)
00390         odb_init(&to->files[i]);
00391 
00392     opd_ext_sfile_dup(to, from);
00393 
00394     for (i = 0; i < CG_HASH_SIZE; ++i)
00395         list_init(&to->cg_hash[i]);
00396 
00397     list_init(&to->hash);
00398     list_init(&to->lru);
00399 }
00400 
00401 
00402 static odb_t * get_file(struct transient const * trans, int is_cg)
00403 {
00404     struct sfile * sf = trans->current;
00405     struct sfile * last = trans->last;
00406     struct cg_entry * cg;
00407     struct list_head * pos;
00408     unsigned long hash;
00409     odb_t * file;
00410 
00411     if ((trans->ext) != NULL)
00412         return opd_ext_sfile_get(trans, is_cg);
00413 
00414     if (trans->event >= op_nr_counters) {
00415         fprintf(stderr, "%s: Invalid counter %lu\n", __FUNCTION__,
00416             trans->event);
00417         abort();
00418     }
00419 
00420     file = &sf->files[trans->event];
00421 
00422     if (!is_cg)
00423         goto open;
00424 
00425     hash = last->hashval & (CG_HASH_SIZE - 1);
00426 
00427     /* Need to look for the right 'to'. Since we're looking for
00428      * 'last', we use its hash.
00429      */
00430     list_for_each(pos, &sf->cg_hash[hash]) {
00431         cg = list_entry(pos, struct cg_entry, hash);
00432         if (sfile_equal(last, &cg->to)) {
00433             file = &cg->to.files[trans->event];
00434             goto open;
00435         }
00436     }
00437 
00438     cg = xmalloc(sizeof(struct cg_entry));
00439     sfile_dup(&cg->to, last);
00440     list_add(&cg->hash, &sf->cg_hash[hash]);
00441     file = &cg->to.files[trans->event];
00442 
00443 open:
00444     if (!odb_open_count(file))
00445         opd_open_sample_file(file, last, sf, trans->event, is_cg);
00446 
00447     /* Error is logged by opd_open_sample_file */
00448     if (!odb_open_count(file))
00449         return NULL;
00450 
00451     return file;
00452 }
00453 
00454 
00455 static void verbose_print_sample(struct sfile * sf, vma_t pc, uint counter)
00456 {
00457     char const * app = verbose_cookie(sf->app_cookie);
00458     printf("0x%llx(%u): ", pc, counter);
00459     if (sf->anon) {
00460         printf("anon (tgid %u, 0x%llx-0x%llx), ",
00461                (unsigned int)sf->anon->tgid,
00462                sf->anon->start, sf->anon->end);
00463     } else if (sf->kernel) {
00464         printf("kern (name %s, 0x%llx-0x%llx), ", sf->kernel->name,
00465                sf->kernel->start, sf->kernel->end);
00466     } else {
00467         printf("%s(%llx), ", verbose_cookie(sf->cookie),  sf->cookie);
00468     }
00469     printf("app %s(%llx)", app, sf->app_cookie);
00470 }
00471 
00472 
00473 static void verbose_sample(struct transient const * trans, vma_t pc)
00474 {
00475     printf("Sample ");
00476     verbose_print_sample(trans->current, pc, trans->event);
00477     printf("\n");
00478 }
00479 
00480 
00481 static void
00482 verbose_arc(struct transient const * trans, vma_t from, vma_t to)
00483 {
00484     printf("Arc ");
00485     verbose_print_sample(trans->current, from, trans->event);
00486     printf(" -> 0x%llx", to);
00487     printf("\n");
00488 }
00489 
00490 
00491 static void sfile_log_arc(struct transient const * trans)
00492 {
00493     int err;
00494     vma_t from = trans->pc;
00495     vma_t to = trans->last_pc;
00496     uint64_t key;
00497     odb_t * file;
00498 
00499     file = get_file(trans, 1);
00500 
00501     /* absolute value -> offset */
00502     if (trans->current->kernel)
00503         from -= trans->current->kernel->start;
00504 
00505     if (trans->last->kernel)
00506         to -= trans->last->kernel->start;
00507 
00508     if (trans->current->anon)
00509         from -= trans->current->anon->start;
00510 
00511     if (trans->last->anon)
00512         to -= trans->last->anon->start;
00513 
00514     if (varcs)
00515         verbose_arc(trans, from, to);
00516 
00517     if (!file) {
00518         opd_stats[OPD_LOST_SAMPLEFILE]++;
00519         return;
00520     }
00521 
00522     /* Possible narrowings to 32-bit value only. */
00523     key = to & (0xffffffff);
00524     key |= ((uint64_t)from) << 32;
00525 
00526     err = odb_update_node(file, key);
00527     if (err) {
00528         fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
00529         abort();
00530     }
00531 }
00532 
00533 
00534 void sfile_log_sample(struct transient const * trans)
00535 {
00536     sfile_log_sample_count(trans, 1);
00537 }
00538 
00539 
00540 void sfile_log_sample_count(struct transient const * trans,
00541                             unsigned long int count)
00542 {
00543     int err;
00544     vma_t pc = trans->pc;
00545     odb_t * file;
00546 
00547     if (trans->tracing == TRACING_ON) {
00548         /* can happen if kernel sample falls through the cracks,
00549          * see opd_put_sample() */
00550         if (trans->last)
00551             sfile_log_arc(trans);
00552         return;
00553     }
00554 
00555     file = get_file(trans, 0);
00556 
00557     /* absolute value -> offset */
00558     if (trans->current->kernel)
00559         pc -= trans->current->kernel->start;
00560 
00561     if (trans->current->anon)
00562         pc -= trans->current->anon->start;
00563 
00564     if (vsamples)
00565         verbose_sample(trans, pc);
00566 
00567     if (!file) {
00568         opd_stats[OPD_LOST_SAMPLEFILE]++;
00569         return;
00570     }
00571 
00572     err = odb_update_node_with_offset(file,
00573                       (odb_key_t)pc,
00574                       count);
00575     if (err) {
00576         fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
00577         abort();
00578     }
00579 }
00580 
00581 
00582 static int close_sfile(struct sfile * sf, void * data __attribute__((unused)))
00583 {
00584     size_t i;
00585 
00586     /* it's OK to close a non-open odb file */
00587     for (i = 0; i < op_nr_counters; ++i)
00588         odb_close(&sf->files[i]);
00589 
00590     opd_ext_sfile_close(sf);
00591 
00592     return 0;
00593 }
00594 
00595 
00596 static void kill_sfile(struct sfile * sf)
00597 {
00598     close_sfile(sf, NULL);
00599     list_del(&sf->hash);
00600     list_del(&sf->lru);
00601 }
00602 
00603 
00604 static int sync_sfile(struct sfile * sf, void * data __attribute__((unused)))
00605 {
00606     size_t i;
00607 
00608     for (i = 0; i < op_nr_counters; ++i)
00609         odb_sync(&sf->files[i]);
00610 
00611     opd_ext_sfile_sync(sf);
00612 
00613     return 0;
00614 }
00615 
00616 
00617 static int is_sfile_kernel(struct sfile * sf, void * data __attribute__((unused)))
00618 {
00619     return !!sf->kernel;
00620 }
00621 
00622 
00623 static int is_sfile_anon(struct sfile * sf, void * data)
00624 {
00625     return sf->anon == data;
00626 }
00627 
00628 
00629 typedef int (*sfile_func)(struct sfile *, void *);
00630 
00631 static void
00632 for_one_sfile(struct sfile * sf, sfile_func func, void * data)
00633 {
00634     size_t i;
00635     int free_sf = func(sf, data);
00636 
00637     for (i = 0; i < CG_HASH_SIZE; ++i) {
00638         struct list_head * pos;
00639         struct list_head * pos2;
00640         list_for_each_safe(pos, pos2, &sf->cg_hash[i]) {
00641             struct cg_entry * cg =
00642                 list_entry(pos, struct cg_entry, hash);
00643             if (free_sf || func(&cg->to, data)) {
00644                 kill_sfile(&cg->to);
00645                 list_del(&cg->hash);
00646                 free(cg);
00647             }
00648         }
00649     }
00650 
00651     if (free_sf) {
00652         kill_sfile(sf);
00653         free(sf);
00654     }
00655 }
00656 
00657 
00658 static void for_each_sfile(sfile_func func, void * data)
00659 {
00660     struct list_head * pos;
00661     struct list_head * pos2;
00662 
00663     list_for_each_safe(pos, pos2, &lru_list) {
00664         struct sfile * sf = list_entry(pos, struct sfile, lru);
00665         for_one_sfile(sf, func, data);
00666     }
00667 }
00668 
00669 
00670 void sfile_clear_kernel(void)
00671 {
00672     for_each_sfile(is_sfile_kernel, NULL);
00673 }
00674 
00675 
00676 void sfile_clear_anon(struct anon_mapping * anon)
00677 {
00678     for_each_sfile(is_sfile_anon, anon);
00679 }
00680 
00681 
00682 void sfile_sync_files(void)
00683 {
00684     for_each_sfile(sync_sfile, NULL);
00685 }
00686 
00687 
00688 void sfile_close_files(void)
00689 {
00690     for_each_sfile(close_sfile, NULL);
00691 }
00692 
00693 
00694 static int always_true(void)
00695 {
00696     return 1;
00697 }
00698 
00699 
00700 #define LRU_AMOUNT 256
00701 
00702 /*
00703  * Clear out older sfiles. Note the current sfiles we're using
00704  * will not be present in this list, due to sfile_get/put() pairs
00705  * around the caller of this.
00706  */
00707 int sfile_lru_clear(void)
00708 {
00709     struct list_head * pos;
00710     struct list_head * pos2;
00711     int amount = LRU_AMOUNT;
00712 
00713     if (list_empty(&lru_list))
00714         return 1;
00715 
00716     list_for_each_safe(pos, pos2, &lru_list) {
00717         struct sfile * sf;
00718         if (!--amount)
00719             break;
00720         sf = list_entry(pos, struct sfile, lru);
00721         for_one_sfile(sf, (sfile_func)always_true, NULL);
00722     }
00723 
00724     return 0;
00725 }
00726 
00727 
00728 void sfile_get(struct sfile * sf)
00729 {
00730     if (sf)
00731         list_del(&sf->lru);
00732 }
00733 
00734 
00735 void sfile_put(struct sfile * sf)
00736 {
00737     if (sf)
00738         list_add_tail(&sf->lru, &lru_list);
00739 }
00740 
00741 
00742 void sfile_init(void)
00743 {
00744     size_t i = 0;
00745 
00746     for (; i < HASH_SIZE; ++i) {
00747         list_init(&hashes[i]);
00748         list_init(&kernel_cmdlines[i]);
00749     }
00750 }

Generated on 8 Nov 2012 for Oprofile by  doxygen 1.6.1