The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/gnu/gcov/gcov_fs.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 // SPDX-License-Identifier: GPL-2.0
    2 // This program is free software; you can redistribute it and/or
    3 // modify it under the terms of the GNU General Public License
    4 // as published by the Free Software Foundation; version 2.
    5 //
    6 // This program is distributed in the hope that it will be useful,
    7 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    9 // GNU General Public License for more details.
   10 //
   11 // You should have received a copy of the GNU General Public License
   12 // along with this program; if not, write to the Free Software
   13 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   14 // 02110-1301, USA.
   15 /*
   16  *  This code exports profiling data as debugfs files to userspace.
   17  *
   18  *    Copyright IBM Corp. 2009
   19  *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
   20  *
   21  *    Uses gcc-internal data definitions.
   22  *    Based on the gcov-kernel patch by:
   23  *               Hubertus Franke <frankeh@us.ibm.com>
   24  *               Nigel Hinds <nhinds@us.ibm.com>
   25  *               Rajan Ravindran <rajancr@us.ibm.com>
   26  *               Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
   27  *               Paul Larson
   28  *               Yi CDL Yang
   29  */
   30 
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/types.h>
   36 #include <sys/systm.h>
   37 #include <sys/param.h>
   38 #include <sys/sbuf.h>
   39 
   40 #include <sys/queue.h>
   41 #include <sys/linker.h>
   42 #include <sys/module.h>
   43 #include <sys/eventhandler.h>
   44 #include <sys/kernel.h>
   45 #include <sys/malloc.h>
   46 #include <sys/syslog.h>
   47 #include <sys/proc.h>
   48 #include <sys/sched.h>
   49 #include <sys/syslog.h>
   50 #include <sys/sysctl.h>
   51 #include <linux/debugfs.h>
   52 
   53 #include <gnu/gcov/gcov.h>
   54 #include <sys/queue.h>
   55 
   56 extern int gcov_events_enabled;
   57 static int gcov_persist;
   58 static struct mtx gcov_mtx;
   59 MTX_SYSINIT(gcov_init, &gcov_mtx, "gcov_mtx", MTX_DEF);
   60 MALLOC_DEFINE(M_GCOV, "gcov", "gcov");
   61 
   62 void __gcov_init(struct gcov_info *info);
   63 void __gcov_flush(void);
   64 void __gcov_merge_add(gcov_type *counters, unsigned int n_counters);
   65 void __gcov_merge_single(gcov_type *counters, unsigned int n_counters);
   66 void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters);
   67 void __gcov_merge_ior(gcov_type *counters, unsigned int n_counters);
   68 void __gcov_merge_time_profile(gcov_type *counters, unsigned int n_counters);
   69 void __gcov_merge_icall_topn(gcov_type *counters, unsigned int n_counters);
   70 void __gcov_exit(void);
   71 
   72 static void gcov_event(enum gcov_action action, struct gcov_info *info);
   73 
   74 
   75 /*
   76  * Private copy taken from libc
   77  */
   78 static char *
   79 (basename)(char *path)
   80 {
   81         char *ptr;
   82 
   83         /*
   84          * If path is a null pointer or points to an empty string,
   85          * basename() shall return a pointer to the string ".".
   86          */
   87         if (path == NULL || *path == '\0')
   88                 return (__DECONST(char *, "."));
   89 
   90         /* Find end of last pathname component and null terminate it. */
   91         ptr = path + strlen(path);
   92         while (ptr > path + 1 && *(ptr - 1) == '/')
   93                 --ptr;
   94         *ptr-- = '\0';
   95 
   96         /* Find beginning of last pathname component. */
   97         while (ptr > path && *(ptr - 1) != '/')
   98                 --ptr;
   99         return (ptr);
  100 }
  101 
  102 /*
  103  * __gcov_init is called by gcc-generated constructor code for each object
  104  * file compiled with -fprofile-arcs.
  105  */
  106 void
  107 __gcov_init(struct gcov_info *info)
  108 {
  109         static unsigned int gcov_version;
  110 
  111         mtx_lock(&gcov_mtx);
  112         if (gcov_version == 0) {
  113                 gcov_version = gcov_info_version(info);
  114                 /*
  115                  * Printing gcc's version magic may prove useful for debugging
  116                  * incompatibility reports.
  117                  */
  118                 log(LOG_INFO, "version magic: 0x%x\n", gcov_version);
  119         }
  120         /*
  121          * Add new profiling data structure to list and inform event
  122          * listener.
  123          */
  124         gcov_info_link(info);
  125         if (gcov_events_enabled)
  126                 gcov_event(GCOV_ADD, info);
  127         mtx_unlock(&gcov_mtx);
  128 }
  129 
  130 /*
  131  * These functions may be referenced by gcc-generated profiling code but serve
  132  * no function for kernel profiling.
  133  */
  134 void
  135 __gcov_flush(void)
  136 {
  137         /* Unused. */
  138 }
  139 
  140 void
  141 __gcov_merge_add(gcov_type *counters, unsigned int n_counters)
  142 {
  143         /* Unused. */
  144 }
  145 
  146 void
  147 __gcov_merge_single(gcov_type *counters, unsigned int n_counters)
  148 {
  149         /* Unused. */
  150 }
  151 
  152 void
  153 __gcov_merge_delta(gcov_type *counters, unsigned int n_counters)
  154 {
  155         /* Unused. */
  156 }
  157 
  158 void
  159 __gcov_merge_ior(gcov_type *counters, unsigned int n_counters)
  160 {
  161         /* Unused. */
  162 }
  163 
  164 void
  165 __gcov_merge_time_profile(gcov_type *counters, unsigned int n_counters)
  166 {
  167         /* Unused. */
  168 }
  169 
  170 void
  171 __gcov_merge_icall_topn(gcov_type *counters, unsigned int n_counters)
  172 {
  173         /* Unused. */
  174 }
  175 
  176 void
  177 __gcov_exit(void)
  178 {
  179         /* Unused. */
  180 }
  181 
  182 
  183 /**
  184  * struct gcov_node - represents a debugfs entry
  185  * @entry: list entry for parent's child node list
  186  * @children: child nodes
  187  * @all_entry: list entry for list of all nodes
  188  * @parent: parent node
  189  * @loaded_info: array of pointers to profiling data sets for loaded object
  190  *   files.
  191  * @num_loaded: number of profiling data sets for loaded object files.
  192  * @unloaded_info: accumulated copy of profiling data sets for unloaded
  193  *   object files. Used only when gcov_persist=1.
  194  * @dentry: main debugfs entry, either a directory or data file
  195  * @links: associated symbolic links
  196  * @name: data file basename
  197  *
  198  * struct gcov_node represents an entity within the gcov/ subdirectory
  199  * of debugfs. There are directory and data file nodes. The latter represent
  200  * the actual synthesized data file plus any associated symbolic links which
  201  * are needed by the gcov tool to work correctly.
  202  */
  203 struct gcov_node {
  204         LIST_ENTRY(gcov_node) children_entry;
  205         LIST_ENTRY(gcov_node) all_entry;
  206         struct {
  207                 struct gcov_node *lh_first;
  208         } children;
  209         struct gcov_node *parent;
  210         struct gcov_info **loaded_info;
  211         struct gcov_info *unloaded_info;
  212         struct dentry *dentry;
  213         struct dentry **links;
  214         int num_loaded;
  215         char name[0];
  216 };
  217 
  218 #ifdef notyet
  219 static const char objtree[] = OBJTREE;
  220 static const char srctree[] = SRCTREE;
  221 #else
  222 static const char objtree[] = "";
  223 static const char srctree[] = "";
  224 #endif
  225 static struct gcov_node root_node;
  226 static struct {
  227         struct gcov_node *lh_first;
  228 } all_head;
  229 static struct mtx node_lock;
  230 MTX_SYSINIT(node_init, &node_lock, "node_lock", MTX_DEF);
  231 static void remove_node(struct gcov_node *node);
  232 
  233 /*
  234  * seq_file.start() implementation for gcov data files. Note that the
  235  * gcov_iterator interface is designed to be more restrictive than seq_file
  236  * (no start from arbitrary position, etc.), to simplify the iterator
  237  * implementation.
  238  */
  239 static void *
  240 gcov_seq_start(struct seq_file *seq, off_t *pos)
  241 {
  242         off_t i;
  243 
  244         gcov_iter_start(seq->private);
  245         for (i = 0; i < *pos; i++) {
  246                 if (gcov_iter_next(seq->private))
  247                         return NULL;
  248         }
  249         return seq->private;
  250 }
  251 
  252 /* seq_file.next() implementation for gcov data files. */
  253 static void *
  254 gcov_seq_next(struct seq_file *seq, void *data, off_t *pos)
  255 {
  256         struct gcov_iterator *iter = data;
  257 
  258         if (gcov_iter_next(iter))
  259                 return NULL;
  260         (*pos)++;
  261 
  262         return iter;
  263 }
  264 
  265 /* seq_file.show() implementation for gcov data files. */
  266 static int
  267 gcov_seq_show(struct seq_file *seq, void *data)
  268 {
  269         struct gcov_iterator *iter = data;
  270 
  271         if (gcov_iter_write(iter, seq->buf))
  272                 return (-EINVAL);
  273         return (0);
  274 }
  275 
  276 static void
  277 gcov_seq_stop(struct seq_file *seq, void *data)
  278 {
  279         /* Unused. */
  280 }
  281 
  282 static const struct seq_operations gcov_seq_ops = {
  283         .start  = gcov_seq_start,
  284         .next   = gcov_seq_next,
  285         .show   = gcov_seq_show,
  286         .stop   = gcov_seq_stop,
  287 };
  288 
  289 /*
  290  * Return a profiling data set associated with the given node. This is
  291  * either a data set for a loaded object file or a data set copy in case
  292  * all associated object files have been unloaded.
  293  */
  294 static struct gcov_info *
  295 get_node_info(struct gcov_node *node)
  296 {
  297         if (node->num_loaded > 0)
  298                 return (node->loaded_info[0]);
  299 
  300         return (node->unloaded_info);
  301 }
  302 
  303 /*
  304  * Return a newly allocated profiling data set which contains the sum of
  305  * all profiling data associated with the given node.
  306  */
  307 static struct gcov_info *
  308 get_accumulated_info(struct gcov_node *node)
  309 {
  310         struct gcov_info *info;
  311         int i = 0;
  312 
  313         if (node->unloaded_info)
  314                 info = gcov_info_dup(node->unloaded_info);
  315         else
  316                 info = gcov_info_dup(node->loaded_info[i++]);
  317         if (info == NULL)
  318                 return (NULL);
  319         for (; i < node->num_loaded; i++)
  320                 gcov_info_add(info, node->loaded_info[i]);
  321 
  322         return (info);
  323 }
  324 
  325 /*
  326  * open() implementation for gcov data files. Create a copy of the profiling
  327  * data set and initialize the iterator and seq_file interface.
  328  */
  329 static int
  330 gcov_seq_open(struct inode *inode, struct file *file)
  331 {
  332         struct gcov_node *node = inode->i_private;
  333         struct gcov_iterator *iter;
  334         struct seq_file *seq;
  335         struct gcov_info *info;
  336         int rc = -ENOMEM;
  337 
  338         mtx_lock(&node_lock);
  339         /*
  340          * Read from a profiling data copy to minimize reference tracking
  341          * complexity and concurrent access and to keep accumulating multiple
  342          * profiling data sets associated with one node simple.
  343          */
  344         info = get_accumulated_info(node);
  345         if (info == NULL)
  346                 goto out_unlock;
  347         iter = gcov_iter_new(info);
  348         if (iter == NULL)
  349                 goto err_free_info;
  350         rc = seq_open(file, &gcov_seq_ops);
  351         if (rc)
  352                 goto err_free_iter_info;
  353         seq = file->private_data;
  354         seq->private = iter;
  355 out_unlock:
  356         mtx_unlock(&node_lock);
  357         return (rc);
  358 
  359 err_free_iter_info:
  360         gcov_iter_free(iter);
  361 err_free_info:
  362         gcov_info_free(info);
  363         goto out_unlock;
  364 }
  365 
  366 /*
  367  * release() implementation for gcov data files. Release resources allocated
  368  * by open().
  369  */
  370 static int
  371 gcov_seq_release(struct inode *inode, struct file *file)
  372 {
  373         struct gcov_iterator *iter;
  374         struct gcov_info *info;
  375         struct seq_file *seq;
  376 
  377         seq = file->private_data;
  378         iter = seq->private;
  379         info = gcov_iter_get_info(iter);
  380         gcov_iter_free(iter);
  381         gcov_info_free(info);
  382         seq_release(inode, file);
  383 
  384         return (0);
  385 }
  386 
  387 /*
  388  * Find a node by the associated data file name. Needs to be called with
  389  * node_lock held.
  390  */
  391 static struct gcov_node *
  392 get_node_by_name(const char *name)
  393 {
  394         struct gcov_node *node;
  395         struct gcov_info *info;
  396 
  397         LIST_FOREACH(node, &all_head, all_entry) {
  398                 info = get_node_info(node);
  399                 if (info && (strcmp(gcov_info_filename(info), name) == 0))
  400                         return (node);
  401         }
  402 
  403         return (NULL);
  404 }
  405 
  406 /*
  407  * Reset all profiling data associated with the specified node.
  408  */
  409 static void
  410 reset_node(struct gcov_node *node)
  411 {
  412         int i;
  413 
  414         if (node->unloaded_info)
  415                 gcov_info_reset(node->unloaded_info);
  416         for (i = 0; i < node->num_loaded; i++)
  417                 gcov_info_reset(node->loaded_info[i]);
  418 }
  419 
  420 void
  421 gcov_stats_reset(void)
  422 {
  423         struct gcov_node *node;
  424 
  425         mtx_lock(&node_lock);
  426  restart:
  427         LIST_FOREACH(node, &all_head, all_entry) {
  428                 if (node->num_loaded > 0)
  429                         reset_node(node);
  430                 else if (LIST_EMPTY(&node->children)) {
  431                         remove_node(node);
  432                         goto restart;
  433                 }
  434         }
  435         mtx_unlock(&node_lock);
  436 }
  437 
  438 /*
  439  * write() implementation for gcov data files. Reset profiling data for the
  440  * corresponding file. If all associated object files have been unloaded,
  441  * remove the debug fs node as well.
  442  */
  443 static ssize_t
  444 gcov_seq_write(struct file *file, const char *addr, size_t len, off_t *pos)
  445 {
  446         struct seq_file *seq;
  447         struct gcov_info *info;
  448         struct gcov_node *node;
  449 
  450         seq = file->private_data;
  451         info = gcov_iter_get_info(seq->private);
  452         mtx_lock(&node_lock);
  453         node = get_node_by_name(gcov_info_filename(info));
  454         if (node) {
  455                 /* Reset counts or remove node for unloaded modules. */
  456                 if (node->num_loaded == 0)
  457                         remove_node(node);
  458                 else
  459                         reset_node(node);
  460         }
  461         /* Reset counts for open file. */
  462         gcov_info_reset(info);
  463         mtx_unlock(&node_lock);
  464 
  465         return (len);
  466 }
  467 
  468 /*
  469  * Given a string <path> representing a file path of format:
  470  *   path/to/file.gcda
  471  * construct and return a new string:
  472  *   <dir/>path/to/file.<ext>
  473  */
  474 static char *
  475 link_target(const char *dir, const char *path, const char *ext)
  476 {
  477         char *target;
  478         char *old_ext;
  479         char *copy;
  480 
  481         copy = strdup_flags(path, M_GCOV, M_NOWAIT);
  482         if (!copy)
  483                 return (NULL);
  484         old_ext = strrchr(copy, '.');
  485         if (old_ext)
  486                 *old_ext = '\0';
  487         target = NULL;
  488         if (dir)
  489                 asprintf(&target, M_GCOV, "%s/%s.%s", dir, copy, ext);
  490         else
  491                 asprintf(&target, M_GCOV, "%s.%s", copy, ext);
  492         free(copy, M_GCOV);
  493 
  494         return (target);
  495 }
  496 
  497 /*
  498  * Construct a string representing the symbolic link target for the given
  499  * gcov data file name and link type. Depending on the link type and the
  500  * location of the data file, the link target can either point to a
  501  * subdirectory of srctree, objtree or in an external location.
  502  */
  503 static char *
  504 get_link_target(const char *filename, const struct gcov_link *ext)
  505 {
  506         const char *rel;
  507         char *result;
  508 
  509         if (strncmp(filename, objtree, strlen(objtree)) == 0) {
  510                 rel = filename + strlen(objtree) + 1;
  511                 if (ext->dir == SRC_TREE)
  512                         result = link_target(srctree, rel, ext->ext);
  513                 else
  514                         result = link_target(objtree, rel, ext->ext);
  515         } else {
  516                 /* External compilation. */
  517                 result = link_target(NULL, filename, ext->ext);
  518         }
  519 
  520         return (result);
  521 }
  522 
  523 #define SKEW_PREFIX     ".tmp_"
  524 
  525 /*
  526  * For a filename .tmp_filename.ext return filename.ext. Needed to compensate
  527  * for filename skewing caused by the mod-versioning mechanism.
  528  */
  529 static const char *
  530 deskew(const char *basename)
  531 {
  532         if (strncmp(basename, SKEW_PREFIX, sizeof(SKEW_PREFIX) - 1) == 0)
  533                 return (basename + sizeof(SKEW_PREFIX) - 1);
  534         return (basename);
  535 }
  536 
  537 /*
  538  * Create links to additional files (usually .c and .gcno files) which the
  539  * gcov tool expects to find in the same directory as the gcov data file.
  540  */
  541 static void
  542 add_links(struct gcov_node *node, struct dentry *parent)
  543 {
  544         const char *path_basename;
  545         char *target;
  546         int num;
  547         int i;
  548 
  549         for (num = 0; gcov_link[num].ext; num++)
  550                 /* Nothing. */;
  551         node->links = malloc((num*sizeof(struct dentry *)), M_GCOV, M_NOWAIT|M_ZERO);
  552         if (node->links == NULL)
  553                 return;
  554         for (i = 0; i < num; i++) {
  555                 target = get_link_target(
  556                                 gcov_info_filename(get_node_info(node)),
  557                                 &gcov_link[i]);
  558                 if (target == NULL)
  559                         goto out_err;
  560                 path_basename = basename(target);
  561                 if (path_basename == target)
  562                         goto out_err;
  563                 node->links[i] = debugfs_create_symlink(deskew(path_basename),
  564                                                         parent, target);
  565                 if (!node->links[i])
  566                         goto out_err;
  567                 free(target, M_GCOV);
  568         }
  569 
  570         return;
  571 out_err:
  572         free(target, M_GCOV);
  573         while (i-- > 0)
  574                 debugfs_remove(node->links[i]);
  575         free(node->links, M_GCOV);
  576         node->links = NULL;
  577 }
  578 
  579 static const struct file_operations gcov_data_fops = {
  580         .open           = gcov_seq_open,
  581         .release        = gcov_seq_release,
  582         .read           = seq_read,
  583         .llseek         = seq_lseek,
  584         .write          = gcov_seq_write,
  585 };
  586 
  587 /* Basic initialization of a new node. */
  588 static void
  589 init_node(struct gcov_node *node, struct gcov_info *info,
  590    const char *name, struct gcov_node *parent)
  591 {
  592         LIST_INIT(&node->children);
  593         if (node->loaded_info) {
  594                 node->loaded_info[0] = info;
  595                 node->num_loaded = 1;
  596         }
  597         node->parent = parent;
  598         if (name)
  599                 strcpy(node->name, name);
  600 }
  601 
  602 /*
  603  * Create a new node and associated debugfs entry. Needs to be called with
  604  * node_lock held.
  605  */
  606 static struct gcov_node *
  607 new_node(struct gcov_node *parent, struct gcov_info *info, const char *name)
  608 {
  609         struct gcov_node *node;
  610 
  611         node = malloc(sizeof(struct gcov_node) + strlen(name) + 1, M_GCOV, M_NOWAIT|M_ZERO);
  612         if (!node)
  613                 goto err_nomem;
  614         if (info) {
  615                 node->loaded_info = malloc(sizeof(struct gcov_info *), M_GCOV, M_NOWAIT|M_ZERO);
  616                 if (!node->loaded_info)
  617                         goto err_nomem;
  618         }
  619         init_node(node, info, name, parent);
  620         /* Differentiate between gcov data file nodes and directory nodes. */
  621         if (info) {
  622                 node->dentry = debugfs_create_file(deskew(node->name), 0600,
  623                                         parent->dentry, node, &gcov_data_fops);
  624         } else
  625                 node->dentry = debugfs_create_dir(node->name, parent->dentry);
  626         if (!node->dentry) {
  627                 log(LOG_WARNING, "could not create file\n");
  628                 free(node, M_GCOV);
  629                 return NULL;
  630         }
  631         if (info)
  632                 add_links(node, parent->dentry);
  633         LIST_INSERT_HEAD(&parent->children, node, children_entry);
  634         LIST_INSERT_HEAD(&all_head, node, all_entry);
  635 
  636         return (node);
  637 
  638 err_nomem:
  639         free(node, M_GCOV);
  640         log(LOG_WARNING, "out of memory\n");
  641         return NULL;
  642 }
  643 
  644 /* Remove symbolic links associated with node. */
  645 static void
  646 remove_links(struct gcov_node *node)
  647 {
  648 
  649         if (node->links == NULL)
  650                 return;
  651         for (int i = 0; gcov_link[i].ext; i++)
  652                 debugfs_remove(node->links[i]);
  653         free(node->links, M_GCOV);
  654         node->links = NULL;
  655 }
  656 
  657 /*
  658  * Remove node from all lists and debugfs and release associated resources.
  659  * Needs to be called with node_lock held.
  660  */
  661 static void
  662 release_node(struct gcov_node *node)
  663 {
  664         LIST_REMOVE(node, children_entry);
  665         LIST_REMOVE(node, all_entry);
  666         debugfs_remove(node->dentry);
  667         remove_links(node);
  668         free(node->loaded_info, M_GCOV);
  669         if (node->unloaded_info)
  670                 gcov_info_free(node->unloaded_info);
  671         free(node, M_GCOV);
  672 }
  673 
  674 /* Release node and empty parents. Needs to be called with node_lock held. */
  675 static void
  676 remove_node(struct gcov_node *node)
  677 {
  678         struct gcov_node *parent;
  679 
  680         while ((node != &root_node) && LIST_EMPTY(&node->children)) {
  681                 parent = node->parent;
  682                 release_node(node);
  683                 node = parent;
  684         }
  685 }
  686 
  687 /*
  688  * Find child node with given basename. Needs to be called with node_lock
  689  * held.
  690  */
  691 static struct gcov_node *
  692 get_child_by_name(struct gcov_node *parent, const char *name)
  693 {
  694         struct gcov_node *node;
  695 
  696         LIST_FOREACH(node, &parent->children, children_entry) {
  697                 if (strcmp(node->name, name) == 0)
  698                         return (node);
  699         }
  700 
  701         return (NULL);
  702 }
  703 
  704 /*
  705  * Create a node for a given profiling data set and add it to all lists and
  706  * debugfs. Needs to be called with node_lock held.
  707  */
  708 static void
  709 add_node(struct gcov_info *info)
  710 {
  711         char *filename;
  712         char *curr;
  713         char *next;
  714         struct gcov_node *parent;
  715         struct gcov_node *node;
  716 
  717         filename = strdup_flags(gcov_info_filename(info), M_GCOV, M_NOWAIT);
  718         if (filename == NULL)
  719                 return;
  720         parent = &root_node;
  721         /* Create directory nodes along the path. */
  722         for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) {
  723                 if (curr == next)
  724                         continue;
  725                 *next = 0;
  726                 if (strcmp(curr, ".") == 0)
  727                         continue;
  728                 if (strcmp(curr, "..") == 0) {
  729                         if (!parent->parent)
  730                                 goto err_remove;
  731                         parent = parent->parent;
  732                         continue;
  733                 }
  734                 node = get_child_by_name(parent, curr);
  735                 if (!node) {
  736                         node = new_node(parent, NULL, curr);
  737                         if (!node)
  738                                 goto err_remove;
  739                 }
  740                 parent = node;
  741         }
  742         /* Create file node. */
  743         node = new_node(parent, info, curr);
  744         if (!node)
  745                 goto err_remove;
  746 out:
  747         free(filename, M_GCOV);
  748         return;
  749 
  750 err_remove:
  751         remove_node(parent);
  752         goto out;
  753 }
  754 
  755 /*
  756  * Associate a profiling data set with an existing node. Needs to be called
  757  * with node_lock held.
  758  */
  759 static void
  760 add_info(struct gcov_node *node, struct gcov_info *info)
  761 {
  762         struct gcov_info **loaded_info;
  763         int num = node->num_loaded;
  764 
  765         /*
  766          * Prepare new array. This is done first to simplify cleanup in
  767          * case the new data set is incompatible, the node only contains
  768          * unloaded data sets and there's not enough memory for the array.
  769          */
  770         loaded_info = malloc((num + 1)* sizeof(struct gcov_info *), M_GCOV, M_NOWAIT|M_ZERO);
  771         if (!loaded_info) {
  772                 log(LOG_WARNING, "could not add '%s' (out of memory)\n",
  773                         gcov_info_filename(info));
  774                 return;
  775         }
  776         memcpy(loaded_info, node->loaded_info,
  777                num * sizeof(struct gcov_info *));
  778         loaded_info[num] = info;
  779         /* Check if the new data set is compatible. */
  780         if (num == 0) {
  781                 /*
  782                  * A module was unloaded, modified and reloaded. The new
  783                  * data set replaces the copy of the last one.
  784                  */
  785                 if (!gcov_info_is_compatible(node->unloaded_info, info)) {
  786                         log(LOG_WARNING, "discarding saved data for %s "
  787                                 "(incompatible version)\n",
  788                                 gcov_info_filename(info));
  789                         gcov_info_free(node->unloaded_info);
  790                         node->unloaded_info = NULL;
  791                 }
  792         } else {
  793                 /*
  794                  * Two different versions of the same object file are loaded.
  795                  * The initial one takes precedence.
  796                  */
  797                 if (!gcov_info_is_compatible(node->loaded_info[0], info)) {
  798                         log(LOG_WARNING, "could not add '%s' (incompatible "
  799                                 "version)\n", gcov_info_filename(info));
  800                         free(loaded_info, M_GCOV);
  801                         return;
  802                 }
  803         }
  804         /* Overwrite previous array. */
  805         free(node->loaded_info, M_GCOV);
  806         node->loaded_info = loaded_info;
  807         node->num_loaded = num + 1;
  808 }
  809 
  810 /*
  811  * Return the index of a profiling data set associated with a node.
  812  */
  813 static int
  814 get_info_index(struct gcov_node *node, struct gcov_info *info)
  815 {
  816         int i;
  817 
  818         for (i = 0; i < node->num_loaded; i++) {
  819                 if (node->loaded_info[i] == info)
  820                         return (i);
  821         }
  822         return (ENOENT);
  823 }
  824 
  825 /*
  826  * Save the data of a profiling data set which is being unloaded.
  827  */
  828 static void
  829 save_info(struct gcov_node *node, struct gcov_info *info)
  830 {
  831         if (node->unloaded_info)
  832                 gcov_info_add(node->unloaded_info, info);
  833         else {
  834                 node->unloaded_info = gcov_info_dup(info);
  835                 if (!node->unloaded_info) {
  836                         log(LOG_WARNING, "could not save data for '%s' "
  837                                 "(out of memory)\n",
  838                                 gcov_info_filename(info));
  839                 }
  840         }
  841 }
  842 
  843 /*
  844  * Disassociate a profiling data set from a node. Needs to be called with
  845  * node_lock held.
  846  */
  847 static void
  848 remove_info(struct gcov_node *node, struct gcov_info *info)
  849 {
  850         int i;
  851 
  852         i = get_info_index(node, info);
  853         if (i < 0) {
  854                 log(LOG_WARNING, "could not remove '%s' (not found)\n",
  855                         gcov_info_filename(info));
  856                 return;
  857         }
  858         if (gcov_persist)
  859                 save_info(node, info);
  860         /* Shrink array. */
  861         node->loaded_info[i] = node->loaded_info[node->num_loaded - 1];
  862         node->num_loaded--;
  863         if (node->num_loaded > 0)
  864                 return;
  865         /* Last loaded data set was removed. */
  866         free(node->loaded_info, M_GCOV);
  867         node->loaded_info = NULL;
  868         node->num_loaded = 0;
  869         if (!node->unloaded_info)
  870                 remove_node(node);
  871 }
  872 
  873 /*
  874  * Callback to create/remove profiling files when code compiled with
  875  * -fprofile-arcs is loaded/unloaded.
  876  */
  877 static void
  878 gcov_event(enum gcov_action action, struct gcov_info *info)
  879 {
  880         struct gcov_node *node;
  881 
  882         mtx_lock(&node_lock);
  883         node = get_node_by_name(gcov_info_filename(info));
  884         switch (action) {
  885         case GCOV_ADD:
  886                 if (node)
  887                         add_info(node, info);
  888                 else
  889                         add_node(info);
  890                 break;
  891         case GCOV_REMOVE:
  892                 if (node)
  893                         remove_info(node, info);
  894                 else {
  895                         log(LOG_WARNING, "could not remove '%s' (not found)\n",
  896                                 gcov_info_filename(info));
  897                 }
  898                 break;
  899         }
  900         mtx_unlock(&node_lock);
  901 }
  902 
  903 /**
  904  * gcov_enable_events - enable event reporting through gcov_event()
  905  *
  906  * Turn on reporting of profiling data load/unload-events through the
  907  * gcov_event() callback. Also replay all previous events once. This function
  908  * is needed because some events are potentially generated too early for the
  909  * callback implementation to handle them initially.
  910  */
  911 void
  912 gcov_enable_events(void)
  913 {
  914         struct gcov_info *info = NULL;
  915         int count;
  916 
  917         mtx_lock(&gcov_mtx);
  918         count = 0;
  919 
  920         /* Perform event callback for previously registered entries. */
  921         while ((info = gcov_info_next(info))) {
  922                 gcov_event(GCOV_ADD, info);
  923                 sched_relinquish(curthread);
  924                 count++;
  925         }
  926 
  927         mtx_unlock(&gcov_mtx);
  928         printf("%s found %d events\n", __func__, count);
  929 }
  930 
  931 /* Update list and generate events when modules are unloaded. */
  932 void
  933 gcov_module_unload(void *arg __unused, module_t mod)
  934 {
  935         struct gcov_info *info = NULL;
  936         struct gcov_info *prev = NULL;
  937 
  938         mtx_lock(&gcov_mtx );
  939 
  940         /* Remove entries located in module from linked list. */
  941         while ((info = gcov_info_next(info))) {
  942                 if (within_module((vm_offset_t)info, mod)) {
  943                         gcov_info_unlink(prev, info);
  944                         if (gcov_events_enabled)
  945                                 gcov_event(GCOV_REMOVE, info);
  946                 } else
  947                         prev = info;
  948         }
  949 
  950         mtx_unlock(&gcov_mtx);
  951 }
  952 
  953 void
  954 gcov_fs_init(void)
  955 {
  956         init_node(&root_node, NULL, NULL, NULL);
  957         root_node.dentry = debugfs_create_dir("gcov", NULL);
  958 }

Cache object: cfcc54655a59f77fd4a2dbf24e654842


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.