Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  This code exports profiling data as debugfs files to userspace.
0004  *
0005  *    Copyright IBM Corp. 2009
0006  *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
0007  *
0008  *    Uses gcc-internal data definitions.
0009  *    Based on the gcov-kernel patch by:
0010  *       Hubertus Franke <frankeh@us.ibm.com>
0011  *       Nigel Hinds <nhinds@us.ibm.com>
0012  *       Rajan Ravindran <rajancr@us.ibm.com>
0013  *       Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
0014  *       Paul Larson
0015  *       Yi CDL Yang
0016  */
0017 
0018 #define pr_fmt(fmt) "gcov: " fmt
0019 
0020 #include <linux/init.h>
0021 #include <linux/module.h>
0022 #include <linux/debugfs.h>
0023 #include <linux/fs.h>
0024 #include <linux/list.h>
0025 #include <linux/string.h>
0026 #include <linux/slab.h>
0027 #include <linux/mutex.h>
0028 #include <linux/seq_file.h>
0029 #include <linux/mm.h>
0030 #include "gcov.h"
0031 
0032 /**
0033  * struct gcov_node - represents a debugfs entry
0034  * @list: list head for child node list
0035  * @children: child nodes
0036  * @all: list head for list of all nodes
0037  * @parent: parent node
0038  * @loaded_info: array of pointers to profiling data sets for loaded object
0039  *   files.
0040  * @num_loaded: number of profiling data sets for loaded object files.
0041  * @unloaded_info: accumulated copy of profiling data sets for unloaded
0042  *   object files. Used only when gcov_persist=1.
0043  * @dentry: main debugfs entry, either a directory or data file
0044  * @links: associated symbolic links
0045  * @name: data file basename
0046  *
0047  * struct gcov_node represents an entity within the gcov/ subdirectory
0048  * of debugfs. There are directory and data file nodes. The latter represent
0049  * the actual synthesized data file plus any associated symbolic links which
0050  * are needed by the gcov tool to work correctly.
0051  */
0052 struct gcov_node {
0053     struct list_head list;
0054     struct list_head children;
0055     struct list_head all;
0056     struct gcov_node *parent;
0057     struct gcov_info **loaded_info;
0058     struct gcov_info *unloaded_info;
0059     struct dentry *dentry;
0060     struct dentry **links;
0061     int num_loaded;
0062     char name[];
0063 };
0064 
0065 static const char objtree[] = OBJTREE;
0066 static const char srctree[] = SRCTREE;
0067 static struct gcov_node root_node;
0068 static LIST_HEAD(all_head);
0069 static DEFINE_MUTEX(node_lock);
0070 
0071 /* If non-zero, keep copies of profiling data for unloaded modules. */
0072 static int gcov_persist = 1;
0073 
0074 static int __init gcov_persist_setup(char *str)
0075 {
0076     unsigned long val;
0077 
0078     if (kstrtoul(str, 0, &val)) {
0079         pr_warn("invalid gcov_persist parameter '%s'\n", str);
0080         return 0;
0081     }
0082     gcov_persist = val;
0083     pr_info("setting gcov_persist to %d\n", gcov_persist);
0084 
0085     return 1;
0086 }
0087 __setup("gcov_persist=", gcov_persist_setup);
0088 
0089 #define ITER_STRIDE PAGE_SIZE
0090 
0091 /**
0092  * struct gcov_iterator - specifies current file position in logical records
0093  * @info: associated profiling data
0094  * @buffer: buffer containing file data
0095  * @size: size of buffer
0096  * @pos: current position in file
0097  */
0098 struct gcov_iterator {
0099     struct gcov_info *info;
0100     size_t size;
0101     loff_t pos;
0102     char buffer[];
0103 };
0104 
0105 /**
0106  * gcov_iter_new - allocate and initialize profiling data iterator
0107  * @info: profiling data set to be iterated
0108  *
0109  * Return file iterator on success, %NULL otherwise.
0110  */
0111 static struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
0112 {
0113     struct gcov_iterator *iter;
0114     size_t size;
0115 
0116     /* Dry-run to get the actual buffer size. */
0117     size = convert_to_gcda(NULL, info);
0118 
0119     iter = kvmalloc(struct_size(iter, buffer, size), GFP_KERNEL);
0120     if (!iter)
0121         return NULL;
0122 
0123     iter->info = info;
0124     iter->size = size;
0125     convert_to_gcda(iter->buffer, info);
0126 
0127     return iter;
0128 }
0129 
0130 
0131 /**
0132  * gcov_iter_free - free iterator data
0133  * @iter: file iterator
0134  */
0135 static void gcov_iter_free(struct gcov_iterator *iter)
0136 {
0137     kvfree(iter);
0138 }
0139 
0140 /**
0141  * gcov_iter_get_info - return profiling data set for given file iterator
0142  * @iter: file iterator
0143  */
0144 static struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
0145 {
0146     return iter->info;
0147 }
0148 
0149 /**
0150  * gcov_iter_start - reset file iterator to starting position
0151  * @iter: file iterator
0152  */
0153 static void gcov_iter_start(struct gcov_iterator *iter)
0154 {
0155     iter->pos = 0;
0156 }
0157 
0158 /**
0159  * gcov_iter_next - advance file iterator to next logical record
0160  * @iter: file iterator
0161  *
0162  * Return zero if new position is valid, non-zero if iterator has reached end.
0163  */
0164 static int gcov_iter_next(struct gcov_iterator *iter)
0165 {
0166     if (iter->pos < iter->size)
0167         iter->pos += ITER_STRIDE;
0168 
0169     if (iter->pos >= iter->size)
0170         return -EINVAL;
0171 
0172     return 0;
0173 }
0174 
0175 /**
0176  * gcov_iter_write - write data for current pos to seq_file
0177  * @iter: file iterator
0178  * @seq: seq_file handle
0179  *
0180  * Return zero on success, non-zero otherwise.
0181  */
0182 static int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
0183 {
0184     size_t len;
0185 
0186     if (iter->pos >= iter->size)
0187         return -EINVAL;
0188 
0189     len = ITER_STRIDE;
0190     if (iter->pos + len > iter->size)
0191         len = iter->size - iter->pos;
0192 
0193     seq_write(seq, iter->buffer + iter->pos, len);
0194 
0195     return 0;
0196 }
0197 
0198 /*
0199  * seq_file.start() implementation for gcov data files. Note that the
0200  * gcov_iterator interface is designed to be more restrictive than seq_file
0201  * (no start from arbitrary position, etc.), to simplify the iterator
0202  * implementation.
0203  */
0204 static void *gcov_seq_start(struct seq_file *seq, loff_t *pos)
0205 {
0206     loff_t i;
0207 
0208     gcov_iter_start(seq->private);
0209     for (i = 0; i < *pos; i++) {
0210         if (gcov_iter_next(seq->private))
0211             return NULL;
0212     }
0213     return seq->private;
0214 }
0215 
0216 /* seq_file.next() implementation for gcov data files. */
0217 static void *gcov_seq_next(struct seq_file *seq, void *data, loff_t *pos)
0218 {
0219     struct gcov_iterator *iter = data;
0220 
0221     (*pos)++;
0222     if (gcov_iter_next(iter))
0223         return NULL;
0224 
0225     return iter;
0226 }
0227 
0228 /* seq_file.show() implementation for gcov data files. */
0229 static int gcov_seq_show(struct seq_file *seq, void *data)
0230 {
0231     struct gcov_iterator *iter = data;
0232 
0233     if (gcov_iter_write(iter, seq))
0234         return -EINVAL;
0235     return 0;
0236 }
0237 
0238 static void gcov_seq_stop(struct seq_file *seq, void *data)
0239 {
0240     /* Unused. */
0241 }
0242 
0243 static const struct seq_operations gcov_seq_ops = {
0244     .start  = gcov_seq_start,
0245     .next   = gcov_seq_next,
0246     .show   = gcov_seq_show,
0247     .stop   = gcov_seq_stop,
0248 };
0249 
0250 /*
0251  * Return a profiling data set associated with the given node. This is
0252  * either a data set for a loaded object file or a data set copy in case
0253  * all associated object files have been unloaded.
0254  */
0255 static struct gcov_info *get_node_info(struct gcov_node *node)
0256 {
0257     if (node->num_loaded > 0)
0258         return node->loaded_info[0];
0259 
0260     return node->unloaded_info;
0261 }
0262 
0263 /*
0264  * Return a newly allocated profiling data set which contains the sum of
0265  * all profiling data associated with the given node.
0266  */
0267 static struct gcov_info *get_accumulated_info(struct gcov_node *node)
0268 {
0269     struct gcov_info *info;
0270     int i = 0;
0271 
0272     if (node->unloaded_info)
0273         info = gcov_info_dup(node->unloaded_info);
0274     else
0275         info = gcov_info_dup(node->loaded_info[i++]);
0276     if (!info)
0277         return NULL;
0278     for (; i < node->num_loaded; i++)
0279         gcov_info_add(info, node->loaded_info[i]);
0280 
0281     return info;
0282 }
0283 
0284 /*
0285  * open() implementation for gcov data files. Create a copy of the profiling
0286  * data set and initialize the iterator and seq_file interface.
0287  */
0288 static int gcov_seq_open(struct inode *inode, struct file *file)
0289 {
0290     struct gcov_node *node = inode->i_private;
0291     struct gcov_iterator *iter;
0292     struct seq_file *seq;
0293     struct gcov_info *info;
0294     int rc = -ENOMEM;
0295 
0296     mutex_lock(&node_lock);
0297     /*
0298      * Read from a profiling data copy to minimize reference tracking
0299      * complexity and concurrent access and to keep accumulating multiple
0300      * profiling data sets associated with one node simple.
0301      */
0302     info = get_accumulated_info(node);
0303     if (!info)
0304         goto out_unlock;
0305     iter = gcov_iter_new(info);
0306     if (!iter)
0307         goto err_free_info;
0308     rc = seq_open(file, &gcov_seq_ops);
0309     if (rc)
0310         goto err_free_iter_info;
0311     seq = file->private_data;
0312     seq->private = iter;
0313 out_unlock:
0314     mutex_unlock(&node_lock);
0315     return rc;
0316 
0317 err_free_iter_info:
0318     gcov_iter_free(iter);
0319 err_free_info:
0320     gcov_info_free(info);
0321     goto out_unlock;
0322 }
0323 
0324 /*
0325  * release() implementation for gcov data files. Release resources allocated
0326  * by open().
0327  */
0328 static int gcov_seq_release(struct inode *inode, struct file *file)
0329 {
0330     struct gcov_iterator *iter;
0331     struct gcov_info *info;
0332     struct seq_file *seq;
0333 
0334     seq = file->private_data;
0335     iter = seq->private;
0336     info = gcov_iter_get_info(iter);
0337     gcov_iter_free(iter);
0338     gcov_info_free(info);
0339     seq_release(inode, file);
0340 
0341     return 0;
0342 }
0343 
0344 /*
0345  * Find a node by the associated data file name. Needs to be called with
0346  * node_lock held.
0347  */
0348 static struct gcov_node *get_node_by_name(const char *name)
0349 {
0350     struct gcov_node *node;
0351     struct gcov_info *info;
0352 
0353     list_for_each_entry(node, &all_head, all) {
0354         info = get_node_info(node);
0355         if (info && (strcmp(gcov_info_filename(info), name) == 0))
0356             return node;
0357     }
0358 
0359     return NULL;
0360 }
0361 
0362 /*
0363  * Reset all profiling data associated with the specified node.
0364  */
0365 static void reset_node(struct gcov_node *node)
0366 {
0367     int i;
0368 
0369     if (node->unloaded_info)
0370         gcov_info_reset(node->unloaded_info);
0371     for (i = 0; i < node->num_loaded; i++)
0372         gcov_info_reset(node->loaded_info[i]);
0373 }
0374 
0375 static void remove_node(struct gcov_node *node);
0376 
0377 /*
0378  * write() implementation for gcov data files. Reset profiling data for the
0379  * corresponding file. If all associated object files have been unloaded,
0380  * remove the debug fs node as well.
0381  */
0382 static ssize_t gcov_seq_write(struct file *file, const char __user *addr,
0383                   size_t len, loff_t *pos)
0384 {
0385     struct seq_file *seq;
0386     struct gcov_info *info;
0387     struct gcov_node *node;
0388 
0389     seq = file->private_data;
0390     info = gcov_iter_get_info(seq->private);
0391     mutex_lock(&node_lock);
0392     node = get_node_by_name(gcov_info_filename(info));
0393     if (node) {
0394         /* Reset counts or remove node for unloaded modules. */
0395         if (node->num_loaded == 0)
0396             remove_node(node);
0397         else
0398             reset_node(node);
0399     }
0400     /* Reset counts for open file. */
0401     gcov_info_reset(info);
0402     mutex_unlock(&node_lock);
0403 
0404     return len;
0405 }
0406 
0407 /*
0408  * Given a string <path> representing a file path of format:
0409  *   path/to/file.gcda
0410  * construct and return a new string:
0411  *   <dir/>path/to/file.<ext>
0412  */
0413 static char *link_target(const char *dir, const char *path, const char *ext)
0414 {
0415     char *target;
0416     char *old_ext;
0417     char *copy;
0418 
0419     copy = kstrdup(path, GFP_KERNEL);
0420     if (!copy)
0421         return NULL;
0422     old_ext = strrchr(copy, '.');
0423     if (old_ext)
0424         *old_ext = '\0';
0425     if (dir)
0426         target = kasprintf(GFP_KERNEL, "%s/%s.%s", dir, copy, ext);
0427     else
0428         target = kasprintf(GFP_KERNEL, "%s.%s", copy, ext);
0429     kfree(copy);
0430 
0431     return target;
0432 }
0433 
0434 /*
0435  * Construct a string representing the symbolic link target for the given
0436  * gcov data file name and link type. Depending on the link type and the
0437  * location of the data file, the link target can either point to a
0438  * subdirectory of srctree, objtree or in an external location.
0439  */
0440 static char *get_link_target(const char *filename, const struct gcov_link *ext)
0441 {
0442     const char *rel;
0443     char *result;
0444 
0445     if (strncmp(filename, objtree, strlen(objtree)) == 0) {
0446         rel = filename + strlen(objtree) + 1;
0447         if (ext->dir == SRC_TREE)
0448             result = link_target(srctree, rel, ext->ext);
0449         else
0450             result = link_target(objtree, rel, ext->ext);
0451     } else {
0452         /* External compilation. */
0453         result = link_target(NULL, filename, ext->ext);
0454     }
0455 
0456     return result;
0457 }
0458 
0459 #define SKEW_PREFIX ".tmp_"
0460 
0461 /*
0462  * For a filename .tmp_filename.ext return filename.ext. Needed to compensate
0463  * for filename skewing caused by the mod-versioning mechanism.
0464  */
0465 static const char *deskew(const char *basename)
0466 {
0467     if (strncmp(basename, SKEW_PREFIX, sizeof(SKEW_PREFIX) - 1) == 0)
0468         return basename + sizeof(SKEW_PREFIX) - 1;
0469     return basename;
0470 }
0471 
0472 /*
0473  * Create links to additional files (usually .c and .gcno files) which the
0474  * gcov tool expects to find in the same directory as the gcov data file.
0475  */
0476 static void add_links(struct gcov_node *node, struct dentry *parent)
0477 {
0478     const char *basename;
0479     char *target;
0480     int num;
0481     int i;
0482 
0483     for (num = 0; gcov_link[num].ext; num++)
0484         /* Nothing. */;
0485     node->links = kcalloc(num, sizeof(struct dentry *), GFP_KERNEL);
0486     if (!node->links)
0487         return;
0488     for (i = 0; i < num; i++) {
0489         target = get_link_target(
0490                 gcov_info_filename(get_node_info(node)),
0491                 &gcov_link[i]);
0492         if (!target)
0493             goto out_err;
0494         basename = kbasename(target);
0495         if (basename == target)
0496             goto out_err;
0497         node->links[i] = debugfs_create_symlink(deskew(basename),
0498                             parent, target);
0499         kfree(target);
0500     }
0501 
0502     return;
0503 out_err:
0504     kfree(target);
0505     while (i-- > 0)
0506         debugfs_remove(node->links[i]);
0507     kfree(node->links);
0508     node->links = NULL;
0509 }
0510 
0511 static const struct file_operations gcov_data_fops = {
0512     .open       = gcov_seq_open,
0513     .release    = gcov_seq_release,
0514     .read       = seq_read,
0515     .llseek     = seq_lseek,
0516     .write      = gcov_seq_write,
0517 };
0518 
0519 /* Basic initialization of a new node. */
0520 static void init_node(struct gcov_node *node, struct gcov_info *info,
0521               const char *name, struct gcov_node *parent)
0522 {
0523     INIT_LIST_HEAD(&node->list);
0524     INIT_LIST_HEAD(&node->children);
0525     INIT_LIST_HEAD(&node->all);
0526     if (node->loaded_info) {
0527         node->loaded_info[0] = info;
0528         node->num_loaded = 1;
0529     }
0530     node->parent = parent;
0531     if (name)
0532         strcpy(node->name, name);
0533 }
0534 
0535 /*
0536  * Create a new node and associated debugfs entry. Needs to be called with
0537  * node_lock held.
0538  */
0539 static struct gcov_node *new_node(struct gcov_node *parent,
0540                   struct gcov_info *info, const char *name)
0541 {
0542     struct gcov_node *node;
0543 
0544     node = kzalloc(sizeof(struct gcov_node) + strlen(name) + 1, GFP_KERNEL);
0545     if (!node)
0546         goto err_nomem;
0547     if (info) {
0548         node->loaded_info = kcalloc(1, sizeof(struct gcov_info *),
0549                        GFP_KERNEL);
0550         if (!node->loaded_info)
0551             goto err_nomem;
0552     }
0553     init_node(node, info, name, parent);
0554     /* Differentiate between gcov data file nodes and directory nodes. */
0555     if (info) {
0556         node->dentry = debugfs_create_file(deskew(node->name), 0600,
0557                     parent->dentry, node, &gcov_data_fops);
0558     } else
0559         node->dentry = debugfs_create_dir(node->name, parent->dentry);
0560     if (info)
0561         add_links(node, parent->dentry);
0562     list_add(&node->list, &parent->children);
0563     list_add(&node->all, &all_head);
0564 
0565     return node;
0566 
0567 err_nomem:
0568     kfree(node);
0569     pr_warn("out of memory\n");
0570     return NULL;
0571 }
0572 
0573 /* Remove symbolic links associated with node. */
0574 static void remove_links(struct gcov_node *node)
0575 {
0576     int i;
0577 
0578     if (!node->links)
0579         return;
0580     for (i = 0; gcov_link[i].ext; i++)
0581         debugfs_remove(node->links[i]);
0582     kfree(node->links);
0583     node->links = NULL;
0584 }
0585 
0586 /*
0587  * Remove node from all lists and debugfs and release associated resources.
0588  * Needs to be called with node_lock held.
0589  */
0590 static void release_node(struct gcov_node *node)
0591 {
0592     list_del(&node->list);
0593     list_del(&node->all);
0594     debugfs_remove(node->dentry);
0595     remove_links(node);
0596     kfree(node->loaded_info);
0597     if (node->unloaded_info)
0598         gcov_info_free(node->unloaded_info);
0599     kfree(node);
0600 }
0601 
0602 /* Release node and empty parents. Needs to be called with node_lock held. */
0603 static void remove_node(struct gcov_node *node)
0604 {
0605     struct gcov_node *parent;
0606 
0607     while ((node != &root_node) && list_empty(&node->children)) {
0608         parent = node->parent;
0609         release_node(node);
0610         node = parent;
0611     }
0612 }
0613 
0614 /*
0615  * Find child node with given basename. Needs to be called with node_lock
0616  * held.
0617  */
0618 static struct gcov_node *get_child_by_name(struct gcov_node *parent,
0619                        const char *name)
0620 {
0621     struct gcov_node *node;
0622 
0623     list_for_each_entry(node, &parent->children, list) {
0624         if (strcmp(node->name, name) == 0)
0625             return node;
0626     }
0627 
0628     return NULL;
0629 }
0630 
0631 /*
0632  * write() implementation for reset file. Reset all profiling data to zero
0633  * and remove nodes for which all associated object files are unloaded.
0634  */
0635 static ssize_t reset_write(struct file *file, const char __user *addr,
0636                size_t len, loff_t *pos)
0637 {
0638     struct gcov_node *node;
0639 
0640     mutex_lock(&node_lock);
0641 restart:
0642     list_for_each_entry(node, &all_head, all) {
0643         if (node->num_loaded > 0)
0644             reset_node(node);
0645         else if (list_empty(&node->children)) {
0646             remove_node(node);
0647             /* Several nodes may have gone - restart loop. */
0648             goto restart;
0649         }
0650     }
0651     mutex_unlock(&node_lock);
0652 
0653     return len;
0654 }
0655 
0656 /* read() implementation for reset file. Unused. */
0657 static ssize_t reset_read(struct file *file, char __user *addr, size_t len,
0658               loff_t *pos)
0659 {
0660     /* Allow read operation so that a recursive copy won't fail. */
0661     return 0;
0662 }
0663 
0664 static const struct file_operations gcov_reset_fops = {
0665     .write  = reset_write,
0666     .read   = reset_read,
0667     .llseek = noop_llseek,
0668 };
0669 
0670 /*
0671  * Create a node for a given profiling data set and add it to all lists and
0672  * debugfs. Needs to be called with node_lock held.
0673  */
0674 static void add_node(struct gcov_info *info)
0675 {
0676     char *filename;
0677     char *curr;
0678     char *next;
0679     struct gcov_node *parent;
0680     struct gcov_node *node;
0681 
0682     filename = kstrdup(gcov_info_filename(info), GFP_KERNEL);
0683     if (!filename)
0684         return;
0685     parent = &root_node;
0686     /* Create directory nodes along the path. */
0687     for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) {
0688         if (curr == next)
0689             continue;
0690         *next = 0;
0691         if (strcmp(curr, ".") == 0)
0692             continue;
0693         if (strcmp(curr, "..") == 0) {
0694             if (!parent->parent)
0695                 goto err_remove;
0696             parent = parent->parent;
0697             continue;
0698         }
0699         node = get_child_by_name(parent, curr);
0700         if (!node) {
0701             node = new_node(parent, NULL, curr);
0702             if (!node)
0703                 goto err_remove;
0704         }
0705         parent = node;
0706     }
0707     /* Create file node. */
0708     node = new_node(parent, info, curr);
0709     if (!node)
0710         goto err_remove;
0711 out:
0712     kfree(filename);
0713     return;
0714 
0715 err_remove:
0716     remove_node(parent);
0717     goto out;
0718 }
0719 
0720 /*
0721  * Associate a profiling data set with an existing node. Needs to be called
0722  * with node_lock held.
0723  */
0724 static void add_info(struct gcov_node *node, struct gcov_info *info)
0725 {
0726     struct gcov_info **loaded_info;
0727     int num = node->num_loaded;
0728 
0729     /*
0730      * Prepare new array. This is done first to simplify cleanup in
0731      * case the new data set is incompatible, the node only contains
0732      * unloaded data sets and there's not enough memory for the array.
0733      */
0734     loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL);
0735     if (!loaded_info) {
0736         pr_warn("could not add '%s' (out of memory)\n",
0737             gcov_info_filename(info));
0738         return;
0739     }
0740     memcpy(loaded_info, node->loaded_info,
0741            num * sizeof(struct gcov_info *));
0742     loaded_info[num] = info;
0743     /* Check if the new data set is compatible. */
0744     if (num == 0) {
0745         /*
0746          * A module was unloaded, modified and reloaded. The new
0747          * data set replaces the copy of the last one.
0748          */
0749         if (!gcov_info_is_compatible(node->unloaded_info, info)) {
0750             pr_warn("discarding saved data for %s "
0751                 "(incompatible version)\n",
0752                 gcov_info_filename(info));
0753             gcov_info_free(node->unloaded_info);
0754             node->unloaded_info = NULL;
0755         }
0756     } else {
0757         /*
0758          * Two different versions of the same object file are loaded.
0759          * The initial one takes precedence.
0760          */
0761         if (!gcov_info_is_compatible(node->loaded_info[0], info)) {
0762             pr_warn("could not add '%s' (incompatible "
0763                 "version)\n", gcov_info_filename(info));
0764             kfree(loaded_info);
0765             return;
0766         }
0767     }
0768     /* Overwrite previous array. */
0769     kfree(node->loaded_info);
0770     node->loaded_info = loaded_info;
0771     node->num_loaded = num + 1;
0772 }
0773 
0774 /*
0775  * Return the index of a profiling data set associated with a node.
0776  */
0777 static int get_info_index(struct gcov_node *node, struct gcov_info *info)
0778 {
0779     int i;
0780 
0781     for (i = 0; i < node->num_loaded; i++) {
0782         if (node->loaded_info[i] == info)
0783             return i;
0784     }
0785     return -ENOENT;
0786 }
0787 
0788 /*
0789  * Save the data of a profiling data set which is being unloaded.
0790  */
0791 static void save_info(struct gcov_node *node, struct gcov_info *info)
0792 {
0793     if (node->unloaded_info)
0794         gcov_info_add(node->unloaded_info, info);
0795     else {
0796         node->unloaded_info = gcov_info_dup(info);
0797         if (!node->unloaded_info) {
0798             pr_warn("could not save data for '%s' "
0799                 "(out of memory)\n",
0800                 gcov_info_filename(info));
0801         }
0802     }
0803 }
0804 
0805 /*
0806  * Disassociate a profiling data set from a node. Needs to be called with
0807  * node_lock held.
0808  */
0809 static void remove_info(struct gcov_node *node, struct gcov_info *info)
0810 {
0811     int i;
0812 
0813     i = get_info_index(node, info);
0814     if (i < 0) {
0815         pr_warn("could not remove '%s' (not found)\n",
0816             gcov_info_filename(info));
0817         return;
0818     }
0819     if (gcov_persist)
0820         save_info(node, info);
0821     /* Shrink array. */
0822     node->loaded_info[i] = node->loaded_info[node->num_loaded - 1];
0823     node->num_loaded--;
0824     if (node->num_loaded > 0)
0825         return;
0826     /* Last loaded data set was removed. */
0827     kfree(node->loaded_info);
0828     node->loaded_info = NULL;
0829     node->num_loaded = 0;
0830     if (!node->unloaded_info)
0831         remove_node(node);
0832 }
0833 
0834 /*
0835  * Callback to create/remove profiling files when code compiled with
0836  * -fprofile-arcs is loaded/unloaded.
0837  */
0838 void gcov_event(enum gcov_action action, struct gcov_info *info)
0839 {
0840     struct gcov_node *node;
0841 
0842     mutex_lock(&node_lock);
0843     node = get_node_by_name(gcov_info_filename(info));
0844     switch (action) {
0845     case GCOV_ADD:
0846         if (node)
0847             add_info(node, info);
0848         else
0849             add_node(info);
0850         break;
0851     case GCOV_REMOVE:
0852         if (node)
0853             remove_info(node, info);
0854         else {
0855             pr_warn("could not remove '%s' (not found)\n",
0856                 gcov_info_filename(info));
0857         }
0858         break;
0859     }
0860     mutex_unlock(&node_lock);
0861 }
0862 
0863 /* Create debugfs entries. */
0864 static __init int gcov_fs_init(void)
0865 {
0866     init_node(&root_node, NULL, NULL, NULL);
0867     /*
0868      * /sys/kernel/debug/gcov will be parent for the reset control file
0869      * and all profiling files.
0870      */
0871     root_node.dentry = debugfs_create_dir("gcov", NULL);
0872     /*
0873      * Create reset file which resets all profiling counts when written
0874      * to.
0875      */
0876     debugfs_create_file("reset", 0600, root_node.dentry, NULL,
0877                 &gcov_reset_fops);
0878     /* Replay previous events to get our fs hierarchy up-to-date. */
0879     gcov_enable_events();
0880     return 0;
0881 }
0882 device_initcall(gcov_fs_init);