1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 22521f2c2SPeter Oberparleiter /* 32521f2c2SPeter Oberparleiter * This code exports profiling data as debugfs files to userspace. 42521f2c2SPeter Oberparleiter * 52521f2c2SPeter Oberparleiter * Copyright IBM Corp. 2009 62521f2c2SPeter Oberparleiter * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com> 72521f2c2SPeter Oberparleiter * 82521f2c2SPeter Oberparleiter * Uses gcc-internal data definitions. 92521f2c2SPeter Oberparleiter * Based on the gcov-kernel patch by: 102521f2c2SPeter Oberparleiter * Hubertus Franke <frankeh@us.ibm.com> 112521f2c2SPeter Oberparleiter * Nigel Hinds <nhinds@us.ibm.com> 122521f2c2SPeter Oberparleiter * Rajan Ravindran <rajancr@us.ibm.com> 132521f2c2SPeter Oberparleiter * Peter Oberparleiter <oberpar@linux.vnet.ibm.com> 142521f2c2SPeter Oberparleiter * Paul Larson 152521f2c2SPeter Oberparleiter * Yi CDL Yang 162521f2c2SPeter Oberparleiter */ 172521f2c2SPeter Oberparleiter 182521f2c2SPeter Oberparleiter #define pr_fmt(fmt) "gcov: " fmt 192521f2c2SPeter Oberparleiter 202521f2c2SPeter Oberparleiter #include <linux/init.h> 212521f2c2SPeter Oberparleiter #include <linux/module.h> 222521f2c2SPeter Oberparleiter #include <linux/debugfs.h> 232521f2c2SPeter Oberparleiter #include <linux/fs.h> 242521f2c2SPeter Oberparleiter #include <linux/list.h> 252521f2c2SPeter Oberparleiter #include <linux/string.h> 262521f2c2SPeter Oberparleiter #include <linux/slab.h> 272521f2c2SPeter Oberparleiter #include <linux/mutex.h> 282521f2c2SPeter Oberparleiter #include <linux/seq_file.h> 292521f2c2SPeter Oberparleiter #include "gcov.h" 302521f2c2SPeter Oberparleiter 312521f2c2SPeter Oberparleiter /** 322521f2c2SPeter Oberparleiter * struct gcov_node - represents a debugfs entry 332521f2c2SPeter Oberparleiter * @list: list head for child node list 342521f2c2SPeter Oberparleiter * @children: child nodes 352521f2c2SPeter Oberparleiter * @all: list head for list of all nodes 362521f2c2SPeter Oberparleiter * @parent: parent node 3785a0fdfdSPeter Oberparleiter * @loaded_info: array of pointers to profiling data sets for loaded object 3885a0fdfdSPeter Oberparleiter * files. 3985a0fdfdSPeter Oberparleiter * @num_loaded: number of profiling data sets for loaded object files. 4085a0fdfdSPeter Oberparleiter * @unloaded_info: accumulated copy of profiling data sets for unloaded 4185a0fdfdSPeter Oberparleiter * object files. Used only when gcov_persist=1. 422521f2c2SPeter Oberparleiter * @dentry: main debugfs entry, either a directory or data file 432521f2c2SPeter Oberparleiter * @links: associated symbolic links 442521f2c2SPeter Oberparleiter * @name: data file basename 452521f2c2SPeter Oberparleiter * 462521f2c2SPeter Oberparleiter * struct gcov_node represents an entity within the gcov/ subdirectory 472521f2c2SPeter Oberparleiter * of debugfs. There are directory and data file nodes. The latter represent 482521f2c2SPeter Oberparleiter * the actual synthesized data file plus any associated symbolic links which 492521f2c2SPeter Oberparleiter * are needed by the gcov tool to work correctly. 502521f2c2SPeter Oberparleiter */ 512521f2c2SPeter Oberparleiter struct gcov_node { 522521f2c2SPeter Oberparleiter struct list_head list; 532521f2c2SPeter Oberparleiter struct list_head children; 542521f2c2SPeter Oberparleiter struct list_head all; 552521f2c2SPeter Oberparleiter struct gcov_node *parent; 5685a0fdfdSPeter Oberparleiter struct gcov_info **loaded_info; 5785a0fdfdSPeter Oberparleiter struct gcov_info *unloaded_info; 582521f2c2SPeter Oberparleiter struct dentry *dentry; 592521f2c2SPeter Oberparleiter struct dentry **links; 6085a0fdfdSPeter Oberparleiter int num_loaded; 616524d794SGustavo A. R. Silva char name[]; 622521f2c2SPeter Oberparleiter }; 632521f2c2SPeter Oberparleiter 642521f2c2SPeter Oberparleiter static const char objtree[] = OBJTREE; 652521f2c2SPeter Oberparleiter static const char srctree[] = SRCTREE; 662521f2c2SPeter Oberparleiter static struct gcov_node root_node; 672521f2c2SPeter Oberparleiter static LIST_HEAD(all_head); 682521f2c2SPeter Oberparleiter static DEFINE_MUTEX(node_lock); 692521f2c2SPeter Oberparleiter 702521f2c2SPeter Oberparleiter /* If non-zero, keep copies of profiling data for unloaded modules. */ 712521f2c2SPeter Oberparleiter static int gcov_persist = 1; 722521f2c2SPeter Oberparleiter 732521f2c2SPeter Oberparleiter static int __init gcov_persist_setup(char *str) 742521f2c2SPeter Oberparleiter { 752521f2c2SPeter Oberparleiter unsigned long val; 762521f2c2SPeter Oberparleiter 776072ddc8SJingoo Han if (kstrtoul(str, 0, &val)) { 78a5ebb875SAndrew Morton pr_warn("invalid gcov_persist parameter '%s'\n", str); 792521f2c2SPeter Oberparleiter return 0; 802521f2c2SPeter Oberparleiter } 812521f2c2SPeter Oberparleiter gcov_persist = val; 822521f2c2SPeter Oberparleiter pr_info("setting gcov_persist to %d\n", gcov_persist); 832521f2c2SPeter Oberparleiter 842521f2c2SPeter Oberparleiter return 1; 852521f2c2SPeter Oberparleiter } 862521f2c2SPeter Oberparleiter __setup("gcov_persist=", gcov_persist_setup); 872521f2c2SPeter Oberparleiter 882521f2c2SPeter Oberparleiter /* 892521f2c2SPeter Oberparleiter * seq_file.start() implementation for gcov data files. Note that the 902521f2c2SPeter Oberparleiter * gcov_iterator interface is designed to be more restrictive than seq_file 912521f2c2SPeter Oberparleiter * (no start from arbitrary position, etc.), to simplify the iterator 922521f2c2SPeter Oberparleiter * implementation. 932521f2c2SPeter Oberparleiter */ 942521f2c2SPeter Oberparleiter static void *gcov_seq_start(struct seq_file *seq, loff_t *pos) 952521f2c2SPeter Oberparleiter { 962521f2c2SPeter Oberparleiter loff_t i; 972521f2c2SPeter Oberparleiter 982521f2c2SPeter Oberparleiter gcov_iter_start(seq->private); 992521f2c2SPeter Oberparleiter for (i = 0; i < *pos; i++) { 1002521f2c2SPeter Oberparleiter if (gcov_iter_next(seq->private)) 1012521f2c2SPeter Oberparleiter return NULL; 1022521f2c2SPeter Oberparleiter } 1032521f2c2SPeter Oberparleiter return seq->private; 1042521f2c2SPeter Oberparleiter } 1052521f2c2SPeter Oberparleiter 1062521f2c2SPeter Oberparleiter /* seq_file.next() implementation for gcov data files. */ 1072521f2c2SPeter Oberparleiter static void *gcov_seq_next(struct seq_file *seq, void *data, loff_t *pos) 1082521f2c2SPeter Oberparleiter { 1092521f2c2SPeter Oberparleiter struct gcov_iterator *iter = data; 1102521f2c2SPeter Oberparleiter 1112521f2c2SPeter Oberparleiter if (gcov_iter_next(iter)) 1122521f2c2SPeter Oberparleiter return NULL; 1132521f2c2SPeter Oberparleiter (*pos)++; 1142521f2c2SPeter Oberparleiter 1152521f2c2SPeter Oberparleiter return iter; 1162521f2c2SPeter Oberparleiter } 1172521f2c2SPeter Oberparleiter 1182521f2c2SPeter Oberparleiter /* seq_file.show() implementation for gcov data files. */ 1192521f2c2SPeter Oberparleiter static int gcov_seq_show(struct seq_file *seq, void *data) 1202521f2c2SPeter Oberparleiter { 1212521f2c2SPeter Oberparleiter struct gcov_iterator *iter = data; 1222521f2c2SPeter Oberparleiter 1232521f2c2SPeter Oberparleiter if (gcov_iter_write(iter, seq)) 1242521f2c2SPeter Oberparleiter return -EINVAL; 1252521f2c2SPeter Oberparleiter return 0; 1262521f2c2SPeter Oberparleiter } 1272521f2c2SPeter Oberparleiter 1282521f2c2SPeter Oberparleiter static void gcov_seq_stop(struct seq_file *seq, void *data) 1292521f2c2SPeter Oberparleiter { 1302521f2c2SPeter Oberparleiter /* Unused. */ 1312521f2c2SPeter Oberparleiter } 1322521f2c2SPeter Oberparleiter 1332521f2c2SPeter Oberparleiter static const struct seq_operations gcov_seq_ops = { 1342521f2c2SPeter Oberparleiter .start = gcov_seq_start, 1352521f2c2SPeter Oberparleiter .next = gcov_seq_next, 1362521f2c2SPeter Oberparleiter .show = gcov_seq_show, 1372521f2c2SPeter Oberparleiter .stop = gcov_seq_stop, 1382521f2c2SPeter Oberparleiter }; 1392521f2c2SPeter Oberparleiter 1402521f2c2SPeter Oberparleiter /* 14185a0fdfdSPeter Oberparleiter * Return a profiling data set associated with the given node. This is 14285a0fdfdSPeter Oberparleiter * either a data set for a loaded object file or a data set copy in case 14385a0fdfdSPeter Oberparleiter * all associated object files have been unloaded. 1442521f2c2SPeter Oberparleiter */ 1452521f2c2SPeter Oberparleiter static struct gcov_info *get_node_info(struct gcov_node *node) 1462521f2c2SPeter Oberparleiter { 14785a0fdfdSPeter Oberparleiter if (node->num_loaded > 0) 14885a0fdfdSPeter Oberparleiter return node->loaded_info[0]; 1492521f2c2SPeter Oberparleiter 15085a0fdfdSPeter Oberparleiter return node->unloaded_info; 15185a0fdfdSPeter Oberparleiter } 15285a0fdfdSPeter Oberparleiter 15385a0fdfdSPeter Oberparleiter /* 15485a0fdfdSPeter Oberparleiter * Return a newly allocated profiling data set which contains the sum of 15585a0fdfdSPeter Oberparleiter * all profiling data associated with the given node. 15685a0fdfdSPeter Oberparleiter */ 15785a0fdfdSPeter Oberparleiter static struct gcov_info *get_accumulated_info(struct gcov_node *node) 15885a0fdfdSPeter Oberparleiter { 15985a0fdfdSPeter Oberparleiter struct gcov_info *info; 16085a0fdfdSPeter Oberparleiter int i = 0; 16185a0fdfdSPeter Oberparleiter 16285a0fdfdSPeter Oberparleiter if (node->unloaded_info) 16385a0fdfdSPeter Oberparleiter info = gcov_info_dup(node->unloaded_info); 16485a0fdfdSPeter Oberparleiter else 16585a0fdfdSPeter Oberparleiter info = gcov_info_dup(node->loaded_info[i++]); 16685a0fdfdSPeter Oberparleiter if (!info) 16785a0fdfdSPeter Oberparleiter return NULL; 16885a0fdfdSPeter Oberparleiter for (; i < node->num_loaded; i++) 16985a0fdfdSPeter Oberparleiter gcov_info_add(info, node->loaded_info[i]); 17085a0fdfdSPeter Oberparleiter 17185a0fdfdSPeter Oberparleiter return info; 1722521f2c2SPeter Oberparleiter } 1732521f2c2SPeter Oberparleiter 1742521f2c2SPeter Oberparleiter /* 1752521f2c2SPeter Oberparleiter * open() implementation for gcov data files. Create a copy of the profiling 1762521f2c2SPeter Oberparleiter * data set and initialize the iterator and seq_file interface. 1772521f2c2SPeter Oberparleiter */ 1782521f2c2SPeter Oberparleiter static int gcov_seq_open(struct inode *inode, struct file *file) 1792521f2c2SPeter Oberparleiter { 1802521f2c2SPeter Oberparleiter struct gcov_node *node = inode->i_private; 1812521f2c2SPeter Oberparleiter struct gcov_iterator *iter; 1822521f2c2SPeter Oberparleiter struct seq_file *seq; 1832521f2c2SPeter Oberparleiter struct gcov_info *info; 1842521f2c2SPeter Oberparleiter int rc = -ENOMEM; 1852521f2c2SPeter Oberparleiter 1862521f2c2SPeter Oberparleiter mutex_lock(&node_lock); 1872521f2c2SPeter Oberparleiter /* 1882521f2c2SPeter Oberparleiter * Read from a profiling data copy to minimize reference tracking 18985a0fdfdSPeter Oberparleiter * complexity and concurrent access and to keep accumulating multiple 19085a0fdfdSPeter Oberparleiter * profiling data sets associated with one node simple. 1912521f2c2SPeter Oberparleiter */ 19285a0fdfdSPeter Oberparleiter info = get_accumulated_info(node); 1932521f2c2SPeter Oberparleiter if (!info) 1942521f2c2SPeter Oberparleiter goto out_unlock; 1952521f2c2SPeter Oberparleiter iter = gcov_iter_new(info); 1962521f2c2SPeter Oberparleiter if (!iter) 1972521f2c2SPeter Oberparleiter goto err_free_info; 1982521f2c2SPeter Oberparleiter rc = seq_open(file, &gcov_seq_ops); 1992521f2c2SPeter Oberparleiter if (rc) 2002521f2c2SPeter Oberparleiter goto err_free_iter_info; 2012521f2c2SPeter Oberparleiter seq = file->private_data; 2022521f2c2SPeter Oberparleiter seq->private = iter; 2032521f2c2SPeter Oberparleiter out_unlock: 2042521f2c2SPeter Oberparleiter mutex_unlock(&node_lock); 2052521f2c2SPeter Oberparleiter return rc; 2062521f2c2SPeter Oberparleiter 2072521f2c2SPeter Oberparleiter err_free_iter_info: 2082521f2c2SPeter Oberparleiter gcov_iter_free(iter); 2092521f2c2SPeter Oberparleiter err_free_info: 2102521f2c2SPeter Oberparleiter gcov_info_free(info); 2112521f2c2SPeter Oberparleiter goto out_unlock; 2122521f2c2SPeter Oberparleiter } 2132521f2c2SPeter Oberparleiter 2142521f2c2SPeter Oberparleiter /* 2152521f2c2SPeter Oberparleiter * release() implementation for gcov data files. Release resources allocated 2162521f2c2SPeter Oberparleiter * by open(). 2172521f2c2SPeter Oberparleiter */ 2182521f2c2SPeter Oberparleiter static int gcov_seq_release(struct inode *inode, struct file *file) 2192521f2c2SPeter Oberparleiter { 2202521f2c2SPeter Oberparleiter struct gcov_iterator *iter; 2212521f2c2SPeter Oberparleiter struct gcov_info *info; 2222521f2c2SPeter Oberparleiter struct seq_file *seq; 2232521f2c2SPeter Oberparleiter 2242521f2c2SPeter Oberparleiter seq = file->private_data; 2252521f2c2SPeter Oberparleiter iter = seq->private; 2262521f2c2SPeter Oberparleiter info = gcov_iter_get_info(iter); 2272521f2c2SPeter Oberparleiter gcov_iter_free(iter); 2282521f2c2SPeter Oberparleiter gcov_info_free(info); 2292521f2c2SPeter Oberparleiter seq_release(inode, file); 2302521f2c2SPeter Oberparleiter 2312521f2c2SPeter Oberparleiter return 0; 2322521f2c2SPeter Oberparleiter } 2332521f2c2SPeter Oberparleiter 2342521f2c2SPeter Oberparleiter /* 2352521f2c2SPeter Oberparleiter * Find a node by the associated data file name. Needs to be called with 2362521f2c2SPeter Oberparleiter * node_lock held. 2372521f2c2SPeter Oberparleiter */ 2382521f2c2SPeter Oberparleiter static struct gcov_node *get_node_by_name(const char *name) 2392521f2c2SPeter Oberparleiter { 2402521f2c2SPeter Oberparleiter struct gcov_node *node; 2412521f2c2SPeter Oberparleiter struct gcov_info *info; 2422521f2c2SPeter Oberparleiter 2432521f2c2SPeter Oberparleiter list_for_each_entry(node, &all_head, all) { 2442521f2c2SPeter Oberparleiter info = get_node_info(node); 2458cbce376SFrantisek Hrbata if (info && (strcmp(gcov_info_filename(info), name) == 0)) 2462521f2c2SPeter Oberparleiter return node; 2472521f2c2SPeter Oberparleiter } 2482521f2c2SPeter Oberparleiter 2492521f2c2SPeter Oberparleiter return NULL; 2502521f2c2SPeter Oberparleiter } 2512521f2c2SPeter Oberparleiter 25285a0fdfdSPeter Oberparleiter /* 25385a0fdfdSPeter Oberparleiter * Reset all profiling data associated with the specified node. 25485a0fdfdSPeter Oberparleiter */ 25585a0fdfdSPeter Oberparleiter static void reset_node(struct gcov_node *node) 25685a0fdfdSPeter Oberparleiter { 25785a0fdfdSPeter Oberparleiter int i; 25885a0fdfdSPeter Oberparleiter 25985a0fdfdSPeter Oberparleiter if (node->unloaded_info) 26085a0fdfdSPeter Oberparleiter gcov_info_reset(node->unloaded_info); 26185a0fdfdSPeter Oberparleiter for (i = 0; i < node->num_loaded; i++) 26285a0fdfdSPeter Oberparleiter gcov_info_reset(node->loaded_info[i]); 26385a0fdfdSPeter Oberparleiter } 26485a0fdfdSPeter Oberparleiter 2652521f2c2SPeter Oberparleiter static void remove_node(struct gcov_node *node); 2662521f2c2SPeter Oberparleiter 2672521f2c2SPeter Oberparleiter /* 2682521f2c2SPeter Oberparleiter * write() implementation for gcov data files. Reset profiling data for the 26985a0fdfdSPeter Oberparleiter * corresponding file. If all associated object files have been unloaded, 27085a0fdfdSPeter Oberparleiter * remove the debug fs node as well. 2712521f2c2SPeter Oberparleiter */ 2722521f2c2SPeter Oberparleiter static ssize_t gcov_seq_write(struct file *file, const char __user *addr, 2732521f2c2SPeter Oberparleiter size_t len, loff_t *pos) 2742521f2c2SPeter Oberparleiter { 2752521f2c2SPeter Oberparleiter struct seq_file *seq; 2762521f2c2SPeter Oberparleiter struct gcov_info *info; 2772521f2c2SPeter Oberparleiter struct gcov_node *node; 2782521f2c2SPeter Oberparleiter 2792521f2c2SPeter Oberparleiter seq = file->private_data; 2802521f2c2SPeter Oberparleiter info = gcov_iter_get_info(seq->private); 2812521f2c2SPeter Oberparleiter mutex_lock(&node_lock); 2828cbce376SFrantisek Hrbata node = get_node_by_name(gcov_info_filename(info)); 2832521f2c2SPeter Oberparleiter if (node) { 2842521f2c2SPeter Oberparleiter /* Reset counts or remove node for unloaded modules. */ 28585a0fdfdSPeter Oberparleiter if (node->num_loaded == 0) 2862521f2c2SPeter Oberparleiter remove_node(node); 2872521f2c2SPeter Oberparleiter else 28885a0fdfdSPeter Oberparleiter reset_node(node); 2892521f2c2SPeter Oberparleiter } 2902521f2c2SPeter Oberparleiter /* Reset counts for open file. */ 2912521f2c2SPeter Oberparleiter gcov_info_reset(info); 2922521f2c2SPeter Oberparleiter mutex_unlock(&node_lock); 2932521f2c2SPeter Oberparleiter 2942521f2c2SPeter Oberparleiter return len; 2952521f2c2SPeter Oberparleiter } 2962521f2c2SPeter Oberparleiter 2972521f2c2SPeter Oberparleiter /* 2982521f2c2SPeter Oberparleiter * Given a string <path> representing a file path of format: 2992521f2c2SPeter Oberparleiter * path/to/file.gcda 3002521f2c2SPeter Oberparleiter * construct and return a new string: 3012521f2c2SPeter Oberparleiter * <dir/>path/to/file.<ext> 3022521f2c2SPeter Oberparleiter */ 3032521f2c2SPeter Oberparleiter static char *link_target(const char *dir, const char *path, const char *ext) 3042521f2c2SPeter Oberparleiter { 3052521f2c2SPeter Oberparleiter char *target; 3062521f2c2SPeter Oberparleiter char *old_ext; 3072521f2c2SPeter Oberparleiter char *copy; 3082521f2c2SPeter Oberparleiter 3092521f2c2SPeter Oberparleiter copy = kstrdup(path, GFP_KERNEL); 3102521f2c2SPeter Oberparleiter if (!copy) 3112521f2c2SPeter Oberparleiter return NULL; 3122521f2c2SPeter Oberparleiter old_ext = strrchr(copy, '.'); 3132521f2c2SPeter Oberparleiter if (old_ext) 3142521f2c2SPeter Oberparleiter *old_ext = '\0'; 3152521f2c2SPeter Oberparleiter if (dir) 3162521f2c2SPeter Oberparleiter target = kasprintf(GFP_KERNEL, "%s/%s.%s", dir, copy, ext); 3172521f2c2SPeter Oberparleiter else 3182521f2c2SPeter Oberparleiter target = kasprintf(GFP_KERNEL, "%s.%s", copy, ext); 3192521f2c2SPeter Oberparleiter kfree(copy); 3202521f2c2SPeter Oberparleiter 3212521f2c2SPeter Oberparleiter return target; 3222521f2c2SPeter Oberparleiter } 3232521f2c2SPeter Oberparleiter 3242521f2c2SPeter Oberparleiter /* 3252521f2c2SPeter Oberparleiter * Construct a string representing the symbolic link target for the given 3262521f2c2SPeter Oberparleiter * gcov data file name and link type. Depending on the link type and the 3272521f2c2SPeter Oberparleiter * location of the data file, the link target can either point to a 3282521f2c2SPeter Oberparleiter * subdirectory of srctree, objtree or in an external location. 3292521f2c2SPeter Oberparleiter */ 3302521f2c2SPeter Oberparleiter static char *get_link_target(const char *filename, const struct gcov_link *ext) 3312521f2c2SPeter Oberparleiter { 3322521f2c2SPeter Oberparleiter const char *rel; 3332521f2c2SPeter Oberparleiter char *result; 3342521f2c2SPeter Oberparleiter 3352521f2c2SPeter Oberparleiter if (strncmp(filename, objtree, strlen(objtree)) == 0) { 3362521f2c2SPeter Oberparleiter rel = filename + strlen(objtree) + 1; 3372521f2c2SPeter Oberparleiter if (ext->dir == SRC_TREE) 3382521f2c2SPeter Oberparleiter result = link_target(srctree, rel, ext->ext); 3392521f2c2SPeter Oberparleiter else 3402521f2c2SPeter Oberparleiter result = link_target(objtree, rel, ext->ext); 3412521f2c2SPeter Oberparleiter } else { 3422521f2c2SPeter Oberparleiter /* External compilation. */ 3432521f2c2SPeter Oberparleiter result = link_target(NULL, filename, ext->ext); 3442521f2c2SPeter Oberparleiter } 3452521f2c2SPeter Oberparleiter 3462521f2c2SPeter Oberparleiter return result; 3472521f2c2SPeter Oberparleiter } 3482521f2c2SPeter Oberparleiter 3492521f2c2SPeter Oberparleiter #define SKEW_PREFIX ".tmp_" 3502521f2c2SPeter Oberparleiter 3512521f2c2SPeter Oberparleiter /* 3522521f2c2SPeter Oberparleiter * For a filename .tmp_filename.ext return filename.ext. Needed to compensate 3532521f2c2SPeter Oberparleiter * for filename skewing caused by the mod-versioning mechanism. 3542521f2c2SPeter Oberparleiter */ 3552521f2c2SPeter Oberparleiter static const char *deskew(const char *basename) 3562521f2c2SPeter Oberparleiter { 3572521f2c2SPeter Oberparleiter if (strncmp(basename, SKEW_PREFIX, sizeof(SKEW_PREFIX) - 1) == 0) 3582521f2c2SPeter Oberparleiter return basename + sizeof(SKEW_PREFIX) - 1; 3592521f2c2SPeter Oberparleiter return basename; 3602521f2c2SPeter Oberparleiter } 3612521f2c2SPeter Oberparleiter 3622521f2c2SPeter Oberparleiter /* 3632521f2c2SPeter Oberparleiter * Create links to additional files (usually .c and .gcno files) which the 3642521f2c2SPeter Oberparleiter * gcov tool expects to find in the same directory as the gcov data file. 3652521f2c2SPeter Oberparleiter */ 3662521f2c2SPeter Oberparleiter static void add_links(struct gcov_node *node, struct dentry *parent) 3672521f2c2SPeter Oberparleiter { 3681931d433SAndy Shevchenko const char *basename; 3692521f2c2SPeter Oberparleiter char *target; 3702521f2c2SPeter Oberparleiter int num; 3712521f2c2SPeter Oberparleiter int i; 3722521f2c2SPeter Oberparleiter 3732521f2c2SPeter Oberparleiter for (num = 0; gcov_link[num].ext; num++) 3742521f2c2SPeter Oberparleiter /* Nothing. */; 3752521f2c2SPeter Oberparleiter node->links = kcalloc(num, sizeof(struct dentry *), GFP_KERNEL); 3762521f2c2SPeter Oberparleiter if (!node->links) 3772521f2c2SPeter Oberparleiter return; 3782521f2c2SPeter Oberparleiter for (i = 0; i < num; i++) { 3798cbce376SFrantisek Hrbata target = get_link_target( 3808cbce376SFrantisek Hrbata gcov_info_filename(get_node_info(node)), 3812521f2c2SPeter Oberparleiter &gcov_link[i]); 3822521f2c2SPeter Oberparleiter if (!target) 3832521f2c2SPeter Oberparleiter goto out_err; 3841931d433SAndy Shevchenko basename = kbasename(target); 3851931d433SAndy Shevchenko if (basename == target) 3862521f2c2SPeter Oberparleiter goto out_err; 3872521f2c2SPeter Oberparleiter node->links[i] = debugfs_create_symlink(deskew(basename), 3882521f2c2SPeter Oberparleiter parent, target); 3892521f2c2SPeter Oberparleiter kfree(target); 3902521f2c2SPeter Oberparleiter } 3912521f2c2SPeter Oberparleiter 3922521f2c2SPeter Oberparleiter return; 3932521f2c2SPeter Oberparleiter out_err: 3942521f2c2SPeter Oberparleiter kfree(target); 3952521f2c2SPeter Oberparleiter while (i-- > 0) 3962521f2c2SPeter Oberparleiter debugfs_remove(node->links[i]); 3972521f2c2SPeter Oberparleiter kfree(node->links); 3982521f2c2SPeter Oberparleiter node->links = NULL; 3992521f2c2SPeter Oberparleiter } 4002521f2c2SPeter Oberparleiter 4012521f2c2SPeter Oberparleiter static const struct file_operations gcov_data_fops = { 4022521f2c2SPeter Oberparleiter .open = gcov_seq_open, 4032521f2c2SPeter Oberparleiter .release = gcov_seq_release, 4042521f2c2SPeter Oberparleiter .read = seq_read, 4052521f2c2SPeter Oberparleiter .llseek = seq_lseek, 4062521f2c2SPeter Oberparleiter .write = gcov_seq_write, 4072521f2c2SPeter Oberparleiter }; 4082521f2c2SPeter Oberparleiter 4092521f2c2SPeter Oberparleiter /* Basic initialization of a new node. */ 4102521f2c2SPeter Oberparleiter static void init_node(struct gcov_node *node, struct gcov_info *info, 4112521f2c2SPeter Oberparleiter const char *name, struct gcov_node *parent) 4122521f2c2SPeter Oberparleiter { 4132521f2c2SPeter Oberparleiter INIT_LIST_HEAD(&node->list); 4142521f2c2SPeter Oberparleiter INIT_LIST_HEAD(&node->children); 4152521f2c2SPeter Oberparleiter INIT_LIST_HEAD(&node->all); 41685a0fdfdSPeter Oberparleiter if (node->loaded_info) { 41785a0fdfdSPeter Oberparleiter node->loaded_info[0] = info; 41885a0fdfdSPeter Oberparleiter node->num_loaded = 1; 41985a0fdfdSPeter Oberparleiter } 4202521f2c2SPeter Oberparleiter node->parent = parent; 4212521f2c2SPeter Oberparleiter if (name) 4222521f2c2SPeter Oberparleiter strcpy(node->name, name); 4232521f2c2SPeter Oberparleiter } 4242521f2c2SPeter Oberparleiter 4252521f2c2SPeter Oberparleiter /* 4262521f2c2SPeter Oberparleiter * Create a new node and associated debugfs entry. Needs to be called with 4272521f2c2SPeter Oberparleiter * node_lock held. 4282521f2c2SPeter Oberparleiter */ 4292521f2c2SPeter Oberparleiter static struct gcov_node *new_node(struct gcov_node *parent, 4302521f2c2SPeter Oberparleiter struct gcov_info *info, const char *name) 4312521f2c2SPeter Oberparleiter { 4322521f2c2SPeter Oberparleiter struct gcov_node *node; 4332521f2c2SPeter Oberparleiter 4342521f2c2SPeter Oberparleiter node = kzalloc(sizeof(struct gcov_node) + strlen(name) + 1, GFP_KERNEL); 43585a0fdfdSPeter Oberparleiter if (!node) 43685a0fdfdSPeter Oberparleiter goto err_nomem; 43785a0fdfdSPeter Oberparleiter if (info) { 43885a0fdfdSPeter Oberparleiter node->loaded_info = kcalloc(1, sizeof(struct gcov_info *), 43985a0fdfdSPeter Oberparleiter GFP_KERNEL); 44085a0fdfdSPeter Oberparleiter if (!node->loaded_info) 44185a0fdfdSPeter Oberparleiter goto err_nomem; 4422521f2c2SPeter Oberparleiter } 4432521f2c2SPeter Oberparleiter init_node(node, info, name, parent); 4442521f2c2SPeter Oberparleiter /* Differentiate between gcov data file nodes and directory nodes. */ 4452521f2c2SPeter Oberparleiter if (info) { 4462521f2c2SPeter Oberparleiter node->dentry = debugfs_create_file(deskew(node->name), 0600, 4472521f2c2SPeter Oberparleiter parent->dentry, node, &gcov_data_fops); 4482521f2c2SPeter Oberparleiter } else 4492521f2c2SPeter Oberparleiter node->dentry = debugfs_create_dir(node->name, parent->dentry); 4502521f2c2SPeter Oberparleiter if (info) 4512521f2c2SPeter Oberparleiter add_links(node, parent->dentry); 4522521f2c2SPeter Oberparleiter list_add(&node->list, &parent->children); 4532521f2c2SPeter Oberparleiter list_add(&node->all, &all_head); 4542521f2c2SPeter Oberparleiter 4552521f2c2SPeter Oberparleiter return node; 45685a0fdfdSPeter Oberparleiter 45785a0fdfdSPeter Oberparleiter err_nomem: 45885a0fdfdSPeter Oberparleiter kfree(node); 459a5ebb875SAndrew Morton pr_warn("out of memory\n"); 46085a0fdfdSPeter Oberparleiter return NULL; 4612521f2c2SPeter Oberparleiter } 4622521f2c2SPeter Oberparleiter 4632521f2c2SPeter Oberparleiter /* Remove symbolic links associated with node. */ 4642521f2c2SPeter Oberparleiter static void remove_links(struct gcov_node *node) 4652521f2c2SPeter Oberparleiter { 4662521f2c2SPeter Oberparleiter int i; 4672521f2c2SPeter Oberparleiter 4682521f2c2SPeter Oberparleiter if (!node->links) 4692521f2c2SPeter Oberparleiter return; 4702521f2c2SPeter Oberparleiter for (i = 0; gcov_link[i].ext; i++) 4712521f2c2SPeter Oberparleiter debugfs_remove(node->links[i]); 4722521f2c2SPeter Oberparleiter kfree(node->links); 4732521f2c2SPeter Oberparleiter node->links = NULL; 4742521f2c2SPeter Oberparleiter } 4752521f2c2SPeter Oberparleiter 4762521f2c2SPeter Oberparleiter /* 4772521f2c2SPeter Oberparleiter * Remove node from all lists and debugfs and release associated resources. 4782521f2c2SPeter Oberparleiter * Needs to be called with node_lock held. 4792521f2c2SPeter Oberparleiter */ 4802521f2c2SPeter Oberparleiter static void release_node(struct gcov_node *node) 4812521f2c2SPeter Oberparleiter { 4822521f2c2SPeter Oberparleiter list_del(&node->list); 4832521f2c2SPeter Oberparleiter list_del(&node->all); 4842521f2c2SPeter Oberparleiter debugfs_remove(node->dentry); 4852521f2c2SPeter Oberparleiter remove_links(node); 48685a0fdfdSPeter Oberparleiter kfree(node->loaded_info); 48785a0fdfdSPeter Oberparleiter if (node->unloaded_info) 48885a0fdfdSPeter Oberparleiter gcov_info_free(node->unloaded_info); 4892521f2c2SPeter Oberparleiter kfree(node); 4902521f2c2SPeter Oberparleiter } 4912521f2c2SPeter Oberparleiter 4922521f2c2SPeter Oberparleiter /* Release node and empty parents. Needs to be called with node_lock held. */ 4932521f2c2SPeter Oberparleiter static void remove_node(struct gcov_node *node) 4942521f2c2SPeter Oberparleiter { 4952521f2c2SPeter Oberparleiter struct gcov_node *parent; 4962521f2c2SPeter Oberparleiter 4972521f2c2SPeter Oberparleiter while ((node != &root_node) && list_empty(&node->children)) { 4982521f2c2SPeter Oberparleiter parent = node->parent; 4992521f2c2SPeter Oberparleiter release_node(node); 5002521f2c2SPeter Oberparleiter node = parent; 5012521f2c2SPeter Oberparleiter } 5022521f2c2SPeter Oberparleiter } 5032521f2c2SPeter Oberparleiter 5042521f2c2SPeter Oberparleiter /* 5052521f2c2SPeter Oberparleiter * Find child node with given basename. Needs to be called with node_lock 5062521f2c2SPeter Oberparleiter * held. 5072521f2c2SPeter Oberparleiter */ 5082521f2c2SPeter Oberparleiter static struct gcov_node *get_child_by_name(struct gcov_node *parent, 5092521f2c2SPeter Oberparleiter const char *name) 5102521f2c2SPeter Oberparleiter { 5112521f2c2SPeter Oberparleiter struct gcov_node *node; 5122521f2c2SPeter Oberparleiter 5132521f2c2SPeter Oberparleiter list_for_each_entry(node, &parent->children, list) { 5142521f2c2SPeter Oberparleiter if (strcmp(node->name, name) == 0) 5152521f2c2SPeter Oberparleiter return node; 5162521f2c2SPeter Oberparleiter } 5172521f2c2SPeter Oberparleiter 5182521f2c2SPeter Oberparleiter return NULL; 5192521f2c2SPeter Oberparleiter } 5202521f2c2SPeter Oberparleiter 5212521f2c2SPeter Oberparleiter /* 5222521f2c2SPeter Oberparleiter * write() implementation for reset file. Reset all profiling data to zero 52385a0fdfdSPeter Oberparleiter * and remove nodes for which all associated object files are unloaded. 5242521f2c2SPeter Oberparleiter */ 5252521f2c2SPeter Oberparleiter static ssize_t reset_write(struct file *file, const char __user *addr, 5262521f2c2SPeter Oberparleiter size_t len, loff_t *pos) 5272521f2c2SPeter Oberparleiter { 5282521f2c2SPeter Oberparleiter struct gcov_node *node; 5292521f2c2SPeter Oberparleiter 5302521f2c2SPeter Oberparleiter mutex_lock(&node_lock); 5312521f2c2SPeter Oberparleiter restart: 5322521f2c2SPeter Oberparleiter list_for_each_entry(node, &all_head, all) { 53385a0fdfdSPeter Oberparleiter if (node->num_loaded > 0) 53485a0fdfdSPeter Oberparleiter reset_node(node); 5352521f2c2SPeter Oberparleiter else if (list_empty(&node->children)) { 5362521f2c2SPeter Oberparleiter remove_node(node); 5372521f2c2SPeter Oberparleiter /* Several nodes may have gone - restart loop. */ 5382521f2c2SPeter Oberparleiter goto restart; 5392521f2c2SPeter Oberparleiter } 5402521f2c2SPeter Oberparleiter } 5412521f2c2SPeter Oberparleiter mutex_unlock(&node_lock); 5422521f2c2SPeter Oberparleiter 5432521f2c2SPeter Oberparleiter return len; 5442521f2c2SPeter Oberparleiter } 5452521f2c2SPeter Oberparleiter 5462521f2c2SPeter Oberparleiter /* read() implementation for reset file. Unused. */ 5472521f2c2SPeter Oberparleiter static ssize_t reset_read(struct file *file, char __user *addr, size_t len, 5482521f2c2SPeter Oberparleiter loff_t *pos) 5492521f2c2SPeter Oberparleiter { 5502521f2c2SPeter Oberparleiter /* Allow read operation so that a recursive copy won't fail. */ 5512521f2c2SPeter Oberparleiter return 0; 5522521f2c2SPeter Oberparleiter } 5532521f2c2SPeter Oberparleiter 5542521f2c2SPeter Oberparleiter static const struct file_operations gcov_reset_fops = { 5552521f2c2SPeter Oberparleiter .write = reset_write, 5562521f2c2SPeter Oberparleiter .read = reset_read, 5576038f373SArnd Bergmann .llseek = noop_llseek, 5582521f2c2SPeter Oberparleiter }; 5592521f2c2SPeter Oberparleiter 5602521f2c2SPeter Oberparleiter /* 5612521f2c2SPeter Oberparleiter * Create a node for a given profiling data set and add it to all lists and 5622521f2c2SPeter Oberparleiter * debugfs. Needs to be called with node_lock held. 5632521f2c2SPeter Oberparleiter */ 5642521f2c2SPeter Oberparleiter static void add_node(struct gcov_info *info) 5652521f2c2SPeter Oberparleiter { 5662521f2c2SPeter Oberparleiter char *filename; 5672521f2c2SPeter Oberparleiter char *curr; 5682521f2c2SPeter Oberparleiter char *next; 5692521f2c2SPeter Oberparleiter struct gcov_node *parent; 5702521f2c2SPeter Oberparleiter struct gcov_node *node; 5712521f2c2SPeter Oberparleiter 5728cbce376SFrantisek Hrbata filename = kstrdup(gcov_info_filename(info), GFP_KERNEL); 5732521f2c2SPeter Oberparleiter if (!filename) 5742521f2c2SPeter Oberparleiter return; 5752521f2c2SPeter Oberparleiter parent = &root_node; 5762521f2c2SPeter Oberparleiter /* Create directory nodes along the path. */ 5772521f2c2SPeter Oberparleiter for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) { 5782521f2c2SPeter Oberparleiter if (curr == next) 5792521f2c2SPeter Oberparleiter continue; 5802521f2c2SPeter Oberparleiter *next = 0; 5812521f2c2SPeter Oberparleiter if (strcmp(curr, ".") == 0) 5822521f2c2SPeter Oberparleiter continue; 5832521f2c2SPeter Oberparleiter if (strcmp(curr, "..") == 0) { 5842521f2c2SPeter Oberparleiter if (!parent->parent) 5852521f2c2SPeter Oberparleiter goto err_remove; 5862521f2c2SPeter Oberparleiter parent = parent->parent; 5872521f2c2SPeter Oberparleiter continue; 5882521f2c2SPeter Oberparleiter } 5892521f2c2SPeter Oberparleiter node = get_child_by_name(parent, curr); 5902521f2c2SPeter Oberparleiter if (!node) { 5912521f2c2SPeter Oberparleiter node = new_node(parent, NULL, curr); 5922521f2c2SPeter Oberparleiter if (!node) 5932521f2c2SPeter Oberparleiter goto err_remove; 5942521f2c2SPeter Oberparleiter } 5952521f2c2SPeter Oberparleiter parent = node; 5962521f2c2SPeter Oberparleiter } 5972521f2c2SPeter Oberparleiter /* Create file node. */ 5982521f2c2SPeter Oberparleiter node = new_node(parent, info, curr); 5992521f2c2SPeter Oberparleiter if (!node) 6002521f2c2SPeter Oberparleiter goto err_remove; 6012521f2c2SPeter Oberparleiter out: 6022521f2c2SPeter Oberparleiter kfree(filename); 6032521f2c2SPeter Oberparleiter return; 6042521f2c2SPeter Oberparleiter 6052521f2c2SPeter Oberparleiter err_remove: 6062521f2c2SPeter Oberparleiter remove_node(parent); 6072521f2c2SPeter Oberparleiter goto out; 6082521f2c2SPeter Oberparleiter } 6092521f2c2SPeter Oberparleiter 6102521f2c2SPeter Oberparleiter /* 61185a0fdfdSPeter Oberparleiter * Associate a profiling data set with an existing node. Needs to be called 61285a0fdfdSPeter Oberparleiter * with node_lock held. 6132521f2c2SPeter Oberparleiter */ 61485a0fdfdSPeter Oberparleiter static void add_info(struct gcov_node *node, struct gcov_info *info) 6152521f2c2SPeter Oberparleiter { 61685a0fdfdSPeter Oberparleiter struct gcov_info **loaded_info; 61785a0fdfdSPeter Oberparleiter int num = node->num_loaded; 6182521f2c2SPeter Oberparleiter 61985a0fdfdSPeter Oberparleiter /* 62085a0fdfdSPeter Oberparleiter * Prepare new array. This is done first to simplify cleanup in 62185a0fdfdSPeter Oberparleiter * case the new data set is incompatible, the node only contains 62285a0fdfdSPeter Oberparleiter * unloaded data sets and there's not enough memory for the array. 62385a0fdfdSPeter Oberparleiter */ 62485a0fdfdSPeter Oberparleiter loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL); 62585a0fdfdSPeter Oberparleiter if (!loaded_info) { 626a5ebb875SAndrew Morton pr_warn("could not add '%s' (out of memory)\n", 6278cbce376SFrantisek Hrbata gcov_info_filename(info)); 62885a0fdfdSPeter Oberparleiter return; 62985a0fdfdSPeter Oberparleiter } 63085a0fdfdSPeter Oberparleiter memcpy(loaded_info, node->loaded_info, 63185a0fdfdSPeter Oberparleiter num * sizeof(struct gcov_info *)); 63285a0fdfdSPeter Oberparleiter loaded_info[num] = info; 63385a0fdfdSPeter Oberparleiter /* Check if the new data set is compatible. */ 63485a0fdfdSPeter Oberparleiter if (num == 0) { 63585a0fdfdSPeter Oberparleiter /* 63685a0fdfdSPeter Oberparleiter * A module was unloaded, modified and reloaded. The new 63785a0fdfdSPeter Oberparleiter * data set replaces the copy of the last one. 63885a0fdfdSPeter Oberparleiter */ 63985a0fdfdSPeter Oberparleiter if (!gcov_info_is_compatible(node->unloaded_info, info)) { 640a5ebb875SAndrew Morton pr_warn("discarding saved data for %s " 6418cbce376SFrantisek Hrbata "(incompatible version)\n", 6428cbce376SFrantisek Hrbata gcov_info_filename(info)); 64385a0fdfdSPeter Oberparleiter gcov_info_free(node->unloaded_info); 64485a0fdfdSPeter Oberparleiter node->unloaded_info = NULL; 64585a0fdfdSPeter Oberparleiter } 64685a0fdfdSPeter Oberparleiter } else { 64785a0fdfdSPeter Oberparleiter /* 64885a0fdfdSPeter Oberparleiter * Two different versions of the same object file are loaded. 64985a0fdfdSPeter Oberparleiter * The initial one takes precedence. 65085a0fdfdSPeter Oberparleiter */ 65185a0fdfdSPeter Oberparleiter if (!gcov_info_is_compatible(node->loaded_info[0], info)) { 652a5ebb875SAndrew Morton pr_warn("could not add '%s' (incompatible " 6538cbce376SFrantisek Hrbata "version)\n", gcov_info_filename(info)); 65485a0fdfdSPeter Oberparleiter kfree(loaded_info); 65585a0fdfdSPeter Oberparleiter return; 65685a0fdfdSPeter Oberparleiter } 65785a0fdfdSPeter Oberparleiter } 65885a0fdfdSPeter Oberparleiter /* Overwrite previous array. */ 65985a0fdfdSPeter Oberparleiter kfree(node->loaded_info); 66085a0fdfdSPeter Oberparleiter node->loaded_info = loaded_info; 66185a0fdfdSPeter Oberparleiter node->num_loaded = num + 1; 6622521f2c2SPeter Oberparleiter } 6632521f2c2SPeter Oberparleiter 6642521f2c2SPeter Oberparleiter /* 66585a0fdfdSPeter Oberparleiter * Return the index of a profiling data set associated with a node. 6662521f2c2SPeter Oberparleiter */ 66785a0fdfdSPeter Oberparleiter static int get_info_index(struct gcov_node *node, struct gcov_info *info) 6682521f2c2SPeter Oberparleiter { 66985a0fdfdSPeter Oberparleiter int i; 67085a0fdfdSPeter Oberparleiter 67185a0fdfdSPeter Oberparleiter for (i = 0; i < node->num_loaded; i++) { 67285a0fdfdSPeter Oberparleiter if (node->loaded_info[i] == info) 67385a0fdfdSPeter Oberparleiter return i; 6742521f2c2SPeter Oberparleiter } 67585a0fdfdSPeter Oberparleiter return -ENOENT; 67685a0fdfdSPeter Oberparleiter } 67785a0fdfdSPeter Oberparleiter 67885a0fdfdSPeter Oberparleiter /* 67985a0fdfdSPeter Oberparleiter * Save the data of a profiling data set which is being unloaded. 68085a0fdfdSPeter Oberparleiter */ 68185a0fdfdSPeter Oberparleiter static void save_info(struct gcov_node *node, struct gcov_info *info) 68285a0fdfdSPeter Oberparleiter { 68385a0fdfdSPeter Oberparleiter if (node->unloaded_info) 68485a0fdfdSPeter Oberparleiter gcov_info_add(node->unloaded_info, info); 68585a0fdfdSPeter Oberparleiter else { 68685a0fdfdSPeter Oberparleiter node->unloaded_info = gcov_info_dup(info); 68785a0fdfdSPeter Oberparleiter if (!node->unloaded_info) { 688a5ebb875SAndrew Morton pr_warn("could not save data for '%s' " 6898cbce376SFrantisek Hrbata "(out of memory)\n", 6908cbce376SFrantisek Hrbata gcov_info_filename(info)); 69185a0fdfdSPeter Oberparleiter } 69285a0fdfdSPeter Oberparleiter } 69385a0fdfdSPeter Oberparleiter } 69485a0fdfdSPeter Oberparleiter 69585a0fdfdSPeter Oberparleiter /* 69685a0fdfdSPeter Oberparleiter * Disassociate a profiling data set from a node. Needs to be called with 69785a0fdfdSPeter Oberparleiter * node_lock held. 69885a0fdfdSPeter Oberparleiter */ 69985a0fdfdSPeter Oberparleiter static void remove_info(struct gcov_node *node, struct gcov_info *info) 70085a0fdfdSPeter Oberparleiter { 70185a0fdfdSPeter Oberparleiter int i; 70285a0fdfdSPeter Oberparleiter 70385a0fdfdSPeter Oberparleiter i = get_info_index(node, info); 70485a0fdfdSPeter Oberparleiter if (i < 0) { 705a5ebb875SAndrew Morton pr_warn("could not remove '%s' (not found)\n", 7068cbce376SFrantisek Hrbata gcov_info_filename(info)); 70785a0fdfdSPeter Oberparleiter return; 70885a0fdfdSPeter Oberparleiter } 70985a0fdfdSPeter Oberparleiter if (gcov_persist) 71085a0fdfdSPeter Oberparleiter save_info(node, info); 71185a0fdfdSPeter Oberparleiter /* Shrink array. */ 71285a0fdfdSPeter Oberparleiter node->loaded_info[i] = node->loaded_info[node->num_loaded - 1]; 71385a0fdfdSPeter Oberparleiter node->num_loaded--; 71485a0fdfdSPeter Oberparleiter if (node->num_loaded > 0) 71585a0fdfdSPeter Oberparleiter return; 71685a0fdfdSPeter Oberparleiter /* Last loaded data set was removed. */ 71785a0fdfdSPeter Oberparleiter kfree(node->loaded_info); 71885a0fdfdSPeter Oberparleiter node->loaded_info = NULL; 71985a0fdfdSPeter Oberparleiter node->num_loaded = 0; 72085a0fdfdSPeter Oberparleiter if (!node->unloaded_info) 72185a0fdfdSPeter Oberparleiter remove_node(node); 7222521f2c2SPeter Oberparleiter } 7232521f2c2SPeter Oberparleiter 7242521f2c2SPeter Oberparleiter /* 7252521f2c2SPeter Oberparleiter * Callback to create/remove profiling files when code compiled with 7262521f2c2SPeter Oberparleiter * -fprofile-arcs is loaded/unloaded. 7272521f2c2SPeter Oberparleiter */ 7282521f2c2SPeter Oberparleiter void gcov_event(enum gcov_action action, struct gcov_info *info) 7292521f2c2SPeter Oberparleiter { 7302521f2c2SPeter Oberparleiter struct gcov_node *node; 7312521f2c2SPeter Oberparleiter 7322521f2c2SPeter Oberparleiter mutex_lock(&node_lock); 7338cbce376SFrantisek Hrbata node = get_node_by_name(gcov_info_filename(info)); 7342521f2c2SPeter Oberparleiter switch (action) { 7352521f2c2SPeter Oberparleiter case GCOV_ADD: 73685a0fdfdSPeter Oberparleiter if (node) 73785a0fdfdSPeter Oberparleiter add_info(node, info); 73885a0fdfdSPeter Oberparleiter else 7392521f2c2SPeter Oberparleiter add_node(info); 7402521f2c2SPeter Oberparleiter break; 7412521f2c2SPeter Oberparleiter case GCOV_REMOVE: 74285a0fdfdSPeter Oberparleiter if (node) 74385a0fdfdSPeter Oberparleiter remove_info(node, info); 74485a0fdfdSPeter Oberparleiter else { 745a5ebb875SAndrew Morton pr_warn("could not remove '%s' (not found)\n", 7468cbce376SFrantisek Hrbata gcov_info_filename(info)); 7472521f2c2SPeter Oberparleiter } 7482521f2c2SPeter Oberparleiter break; 7492521f2c2SPeter Oberparleiter } 7502521f2c2SPeter Oberparleiter mutex_unlock(&node_lock); 7512521f2c2SPeter Oberparleiter } 7522521f2c2SPeter Oberparleiter 7532521f2c2SPeter Oberparleiter /* Create debugfs entries. */ 7542521f2c2SPeter Oberparleiter static __init int gcov_fs_init(void) 7552521f2c2SPeter Oberparleiter { 7562521f2c2SPeter Oberparleiter init_node(&root_node, NULL, NULL, NULL); 7572521f2c2SPeter Oberparleiter /* 7582521f2c2SPeter Oberparleiter * /sys/kernel/debug/gcov will be parent for the reset control file 7592521f2c2SPeter Oberparleiter * and all profiling files. 7602521f2c2SPeter Oberparleiter */ 7612521f2c2SPeter Oberparleiter root_node.dentry = debugfs_create_dir("gcov", NULL); 7622521f2c2SPeter Oberparleiter /* 7632521f2c2SPeter Oberparleiter * Create reset file which resets all profiling counts when written 7642521f2c2SPeter Oberparleiter * to. 7652521f2c2SPeter Oberparleiter */ 7661c769fc4SGreg Kroah-Hartman debugfs_create_file("reset", 0600, root_node.dentry, NULL, 7671c769fc4SGreg Kroah-Hartman &gcov_reset_fops); 7682521f2c2SPeter Oberparleiter /* Replay previous events to get our fs hierarchy up-to-date. */ 7692521f2c2SPeter Oberparleiter gcov_enable_events(); 7702521f2c2SPeter Oberparleiter return 0; 7712521f2c2SPeter Oberparleiter } 7722521f2c2SPeter Oberparleiter device_initcall(gcov_fs_init); 773