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; 612521f2c2SPeter Oberparleiter char name[0]; 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 struct dentry *reset_dentry; 682521f2c2SPeter Oberparleiter static LIST_HEAD(all_head); 692521f2c2SPeter Oberparleiter static DEFINE_MUTEX(node_lock); 702521f2c2SPeter Oberparleiter 712521f2c2SPeter Oberparleiter /* If non-zero, keep copies of profiling data for unloaded modules. */ 722521f2c2SPeter Oberparleiter static int gcov_persist = 1; 732521f2c2SPeter Oberparleiter 742521f2c2SPeter Oberparleiter static int __init gcov_persist_setup(char *str) 752521f2c2SPeter Oberparleiter { 762521f2c2SPeter Oberparleiter unsigned long val; 772521f2c2SPeter Oberparleiter 786072ddc8SJingoo Han if (kstrtoul(str, 0, &val)) { 79a5ebb875SAndrew Morton pr_warn("invalid gcov_persist parameter '%s'\n", str); 802521f2c2SPeter Oberparleiter return 0; 812521f2c2SPeter Oberparleiter } 822521f2c2SPeter Oberparleiter gcov_persist = val; 832521f2c2SPeter Oberparleiter pr_info("setting gcov_persist to %d\n", gcov_persist); 842521f2c2SPeter Oberparleiter 852521f2c2SPeter Oberparleiter return 1; 862521f2c2SPeter Oberparleiter } 872521f2c2SPeter Oberparleiter __setup("gcov_persist=", gcov_persist_setup); 882521f2c2SPeter Oberparleiter 892521f2c2SPeter Oberparleiter /* 902521f2c2SPeter Oberparleiter * seq_file.start() implementation for gcov data files. Note that the 912521f2c2SPeter Oberparleiter * gcov_iterator interface is designed to be more restrictive than seq_file 922521f2c2SPeter Oberparleiter * (no start from arbitrary position, etc.), to simplify the iterator 932521f2c2SPeter Oberparleiter * implementation. 942521f2c2SPeter Oberparleiter */ 952521f2c2SPeter Oberparleiter static void *gcov_seq_start(struct seq_file *seq, loff_t *pos) 962521f2c2SPeter Oberparleiter { 972521f2c2SPeter Oberparleiter loff_t i; 982521f2c2SPeter Oberparleiter 992521f2c2SPeter Oberparleiter gcov_iter_start(seq->private); 1002521f2c2SPeter Oberparleiter for (i = 0; i < *pos; i++) { 1012521f2c2SPeter Oberparleiter if (gcov_iter_next(seq->private)) 1022521f2c2SPeter Oberparleiter return NULL; 1032521f2c2SPeter Oberparleiter } 1042521f2c2SPeter Oberparleiter return seq->private; 1052521f2c2SPeter Oberparleiter } 1062521f2c2SPeter Oberparleiter 1072521f2c2SPeter Oberparleiter /* seq_file.next() implementation for gcov data files. */ 1082521f2c2SPeter Oberparleiter static void *gcov_seq_next(struct seq_file *seq, void *data, loff_t *pos) 1092521f2c2SPeter Oberparleiter { 1102521f2c2SPeter Oberparleiter struct gcov_iterator *iter = data; 1112521f2c2SPeter Oberparleiter 1122521f2c2SPeter Oberparleiter if (gcov_iter_next(iter)) 1132521f2c2SPeter Oberparleiter return NULL; 1142521f2c2SPeter Oberparleiter (*pos)++; 1152521f2c2SPeter Oberparleiter 1162521f2c2SPeter Oberparleiter return iter; 1172521f2c2SPeter Oberparleiter } 1182521f2c2SPeter Oberparleiter 1192521f2c2SPeter Oberparleiter /* seq_file.show() implementation for gcov data files. */ 1202521f2c2SPeter Oberparleiter static int gcov_seq_show(struct seq_file *seq, void *data) 1212521f2c2SPeter Oberparleiter { 1222521f2c2SPeter Oberparleiter struct gcov_iterator *iter = data; 1232521f2c2SPeter Oberparleiter 1242521f2c2SPeter Oberparleiter if (gcov_iter_write(iter, seq)) 1252521f2c2SPeter Oberparleiter return -EINVAL; 1262521f2c2SPeter Oberparleiter return 0; 1272521f2c2SPeter Oberparleiter } 1282521f2c2SPeter Oberparleiter 1292521f2c2SPeter Oberparleiter static void gcov_seq_stop(struct seq_file *seq, void *data) 1302521f2c2SPeter Oberparleiter { 1312521f2c2SPeter Oberparleiter /* Unused. */ 1322521f2c2SPeter Oberparleiter } 1332521f2c2SPeter Oberparleiter 1342521f2c2SPeter Oberparleiter static const struct seq_operations gcov_seq_ops = { 1352521f2c2SPeter Oberparleiter .start = gcov_seq_start, 1362521f2c2SPeter Oberparleiter .next = gcov_seq_next, 1372521f2c2SPeter Oberparleiter .show = gcov_seq_show, 1382521f2c2SPeter Oberparleiter .stop = gcov_seq_stop, 1392521f2c2SPeter Oberparleiter }; 1402521f2c2SPeter Oberparleiter 1412521f2c2SPeter Oberparleiter /* 14285a0fdfdSPeter Oberparleiter * Return a profiling data set associated with the given node. This is 14385a0fdfdSPeter Oberparleiter * either a data set for a loaded object file or a data set copy in case 14485a0fdfdSPeter Oberparleiter * all associated object files have been unloaded. 1452521f2c2SPeter Oberparleiter */ 1462521f2c2SPeter Oberparleiter static struct gcov_info *get_node_info(struct gcov_node *node) 1472521f2c2SPeter Oberparleiter { 14885a0fdfdSPeter Oberparleiter if (node->num_loaded > 0) 14985a0fdfdSPeter Oberparleiter return node->loaded_info[0]; 1502521f2c2SPeter Oberparleiter 15185a0fdfdSPeter Oberparleiter return node->unloaded_info; 15285a0fdfdSPeter Oberparleiter } 15385a0fdfdSPeter Oberparleiter 15485a0fdfdSPeter Oberparleiter /* 15585a0fdfdSPeter Oberparleiter * Return a newly allocated profiling data set which contains the sum of 15685a0fdfdSPeter Oberparleiter * all profiling data associated with the given node. 15785a0fdfdSPeter Oberparleiter */ 15885a0fdfdSPeter Oberparleiter static struct gcov_info *get_accumulated_info(struct gcov_node *node) 15985a0fdfdSPeter Oberparleiter { 16085a0fdfdSPeter Oberparleiter struct gcov_info *info; 16185a0fdfdSPeter Oberparleiter int i = 0; 16285a0fdfdSPeter Oberparleiter 16385a0fdfdSPeter Oberparleiter if (node->unloaded_info) 16485a0fdfdSPeter Oberparleiter info = gcov_info_dup(node->unloaded_info); 16585a0fdfdSPeter Oberparleiter else 16685a0fdfdSPeter Oberparleiter info = gcov_info_dup(node->loaded_info[i++]); 16785a0fdfdSPeter Oberparleiter if (!info) 16885a0fdfdSPeter Oberparleiter return NULL; 16985a0fdfdSPeter Oberparleiter for (; i < node->num_loaded; i++) 17085a0fdfdSPeter Oberparleiter gcov_info_add(info, node->loaded_info[i]); 17185a0fdfdSPeter Oberparleiter 17285a0fdfdSPeter Oberparleiter return info; 1732521f2c2SPeter Oberparleiter } 1742521f2c2SPeter Oberparleiter 1752521f2c2SPeter Oberparleiter /* 1762521f2c2SPeter Oberparleiter * open() implementation for gcov data files. Create a copy of the profiling 1772521f2c2SPeter Oberparleiter * data set and initialize the iterator and seq_file interface. 1782521f2c2SPeter Oberparleiter */ 1792521f2c2SPeter Oberparleiter static int gcov_seq_open(struct inode *inode, struct file *file) 1802521f2c2SPeter Oberparleiter { 1812521f2c2SPeter Oberparleiter struct gcov_node *node = inode->i_private; 1822521f2c2SPeter Oberparleiter struct gcov_iterator *iter; 1832521f2c2SPeter Oberparleiter struct seq_file *seq; 1842521f2c2SPeter Oberparleiter struct gcov_info *info; 1852521f2c2SPeter Oberparleiter int rc = -ENOMEM; 1862521f2c2SPeter Oberparleiter 1872521f2c2SPeter Oberparleiter mutex_lock(&node_lock); 1882521f2c2SPeter Oberparleiter /* 1892521f2c2SPeter Oberparleiter * Read from a profiling data copy to minimize reference tracking 19085a0fdfdSPeter Oberparleiter * complexity and concurrent access and to keep accumulating multiple 19185a0fdfdSPeter Oberparleiter * profiling data sets associated with one node simple. 1922521f2c2SPeter Oberparleiter */ 19385a0fdfdSPeter Oberparleiter info = get_accumulated_info(node); 1942521f2c2SPeter Oberparleiter if (!info) 1952521f2c2SPeter Oberparleiter goto out_unlock; 1962521f2c2SPeter Oberparleiter iter = gcov_iter_new(info); 1972521f2c2SPeter Oberparleiter if (!iter) 1982521f2c2SPeter Oberparleiter goto err_free_info; 1992521f2c2SPeter Oberparleiter rc = seq_open(file, &gcov_seq_ops); 2002521f2c2SPeter Oberparleiter if (rc) 2012521f2c2SPeter Oberparleiter goto err_free_iter_info; 2022521f2c2SPeter Oberparleiter seq = file->private_data; 2032521f2c2SPeter Oberparleiter seq->private = iter; 2042521f2c2SPeter Oberparleiter out_unlock: 2052521f2c2SPeter Oberparleiter mutex_unlock(&node_lock); 2062521f2c2SPeter Oberparleiter return rc; 2072521f2c2SPeter Oberparleiter 2082521f2c2SPeter Oberparleiter err_free_iter_info: 2092521f2c2SPeter Oberparleiter gcov_iter_free(iter); 2102521f2c2SPeter Oberparleiter err_free_info: 2112521f2c2SPeter Oberparleiter gcov_info_free(info); 2122521f2c2SPeter Oberparleiter goto out_unlock; 2132521f2c2SPeter Oberparleiter } 2142521f2c2SPeter Oberparleiter 2152521f2c2SPeter Oberparleiter /* 2162521f2c2SPeter Oberparleiter * release() implementation for gcov data files. Release resources allocated 2172521f2c2SPeter Oberparleiter * by open(). 2182521f2c2SPeter Oberparleiter */ 2192521f2c2SPeter Oberparleiter static int gcov_seq_release(struct inode *inode, struct file *file) 2202521f2c2SPeter Oberparleiter { 2212521f2c2SPeter Oberparleiter struct gcov_iterator *iter; 2222521f2c2SPeter Oberparleiter struct gcov_info *info; 2232521f2c2SPeter Oberparleiter struct seq_file *seq; 2242521f2c2SPeter Oberparleiter 2252521f2c2SPeter Oberparleiter seq = file->private_data; 2262521f2c2SPeter Oberparleiter iter = seq->private; 2272521f2c2SPeter Oberparleiter info = gcov_iter_get_info(iter); 2282521f2c2SPeter Oberparleiter gcov_iter_free(iter); 2292521f2c2SPeter Oberparleiter gcov_info_free(info); 2302521f2c2SPeter Oberparleiter seq_release(inode, file); 2312521f2c2SPeter Oberparleiter 2322521f2c2SPeter Oberparleiter return 0; 2332521f2c2SPeter Oberparleiter } 2342521f2c2SPeter Oberparleiter 2352521f2c2SPeter Oberparleiter /* 2362521f2c2SPeter Oberparleiter * Find a node by the associated data file name. Needs to be called with 2372521f2c2SPeter Oberparleiter * node_lock held. 2382521f2c2SPeter Oberparleiter */ 2392521f2c2SPeter Oberparleiter static struct gcov_node *get_node_by_name(const char *name) 2402521f2c2SPeter Oberparleiter { 2412521f2c2SPeter Oberparleiter struct gcov_node *node; 2422521f2c2SPeter Oberparleiter struct gcov_info *info; 2432521f2c2SPeter Oberparleiter 2442521f2c2SPeter Oberparleiter list_for_each_entry(node, &all_head, all) { 2452521f2c2SPeter Oberparleiter info = get_node_info(node); 2468cbce376SFrantisek Hrbata if (info && (strcmp(gcov_info_filename(info), name) == 0)) 2472521f2c2SPeter Oberparleiter return node; 2482521f2c2SPeter Oberparleiter } 2492521f2c2SPeter Oberparleiter 2502521f2c2SPeter Oberparleiter return NULL; 2512521f2c2SPeter Oberparleiter } 2522521f2c2SPeter Oberparleiter 25385a0fdfdSPeter Oberparleiter /* 25485a0fdfdSPeter Oberparleiter * Reset all profiling data associated with the specified node. 25585a0fdfdSPeter Oberparleiter */ 25685a0fdfdSPeter Oberparleiter static void reset_node(struct gcov_node *node) 25785a0fdfdSPeter Oberparleiter { 25885a0fdfdSPeter Oberparleiter int i; 25985a0fdfdSPeter Oberparleiter 26085a0fdfdSPeter Oberparleiter if (node->unloaded_info) 26185a0fdfdSPeter Oberparleiter gcov_info_reset(node->unloaded_info); 26285a0fdfdSPeter Oberparleiter for (i = 0; i < node->num_loaded; i++) 26385a0fdfdSPeter Oberparleiter gcov_info_reset(node->loaded_info[i]); 26485a0fdfdSPeter Oberparleiter } 26585a0fdfdSPeter Oberparleiter 2662521f2c2SPeter Oberparleiter static void remove_node(struct gcov_node *node); 2672521f2c2SPeter Oberparleiter 2682521f2c2SPeter Oberparleiter /* 2692521f2c2SPeter Oberparleiter * write() implementation for gcov data files. Reset profiling data for the 27085a0fdfdSPeter Oberparleiter * corresponding file. If all associated object files have been unloaded, 27185a0fdfdSPeter Oberparleiter * remove the debug fs node as well. 2722521f2c2SPeter Oberparleiter */ 2732521f2c2SPeter Oberparleiter static ssize_t gcov_seq_write(struct file *file, const char __user *addr, 2742521f2c2SPeter Oberparleiter size_t len, loff_t *pos) 2752521f2c2SPeter Oberparleiter { 2762521f2c2SPeter Oberparleiter struct seq_file *seq; 2772521f2c2SPeter Oberparleiter struct gcov_info *info; 2782521f2c2SPeter Oberparleiter struct gcov_node *node; 2792521f2c2SPeter Oberparleiter 2802521f2c2SPeter Oberparleiter seq = file->private_data; 2812521f2c2SPeter Oberparleiter info = gcov_iter_get_info(seq->private); 2822521f2c2SPeter Oberparleiter mutex_lock(&node_lock); 2838cbce376SFrantisek Hrbata node = get_node_by_name(gcov_info_filename(info)); 2842521f2c2SPeter Oberparleiter if (node) { 2852521f2c2SPeter Oberparleiter /* Reset counts or remove node for unloaded modules. */ 28685a0fdfdSPeter Oberparleiter if (node->num_loaded == 0) 2872521f2c2SPeter Oberparleiter remove_node(node); 2882521f2c2SPeter Oberparleiter else 28985a0fdfdSPeter Oberparleiter reset_node(node); 2902521f2c2SPeter Oberparleiter } 2912521f2c2SPeter Oberparleiter /* Reset counts for open file. */ 2922521f2c2SPeter Oberparleiter gcov_info_reset(info); 2932521f2c2SPeter Oberparleiter mutex_unlock(&node_lock); 2942521f2c2SPeter Oberparleiter 2952521f2c2SPeter Oberparleiter return len; 2962521f2c2SPeter Oberparleiter } 2972521f2c2SPeter Oberparleiter 2982521f2c2SPeter Oberparleiter /* 2992521f2c2SPeter Oberparleiter * Given a string <path> representing a file path of format: 3002521f2c2SPeter Oberparleiter * path/to/file.gcda 3012521f2c2SPeter Oberparleiter * construct and return a new string: 3022521f2c2SPeter Oberparleiter * <dir/>path/to/file.<ext> 3032521f2c2SPeter Oberparleiter */ 3042521f2c2SPeter Oberparleiter static char *link_target(const char *dir, const char *path, const char *ext) 3052521f2c2SPeter Oberparleiter { 3062521f2c2SPeter Oberparleiter char *target; 3072521f2c2SPeter Oberparleiter char *old_ext; 3082521f2c2SPeter Oberparleiter char *copy; 3092521f2c2SPeter Oberparleiter 3102521f2c2SPeter Oberparleiter copy = kstrdup(path, GFP_KERNEL); 3112521f2c2SPeter Oberparleiter if (!copy) 3122521f2c2SPeter Oberparleiter return NULL; 3132521f2c2SPeter Oberparleiter old_ext = strrchr(copy, '.'); 3142521f2c2SPeter Oberparleiter if (old_ext) 3152521f2c2SPeter Oberparleiter *old_ext = '\0'; 3162521f2c2SPeter Oberparleiter if (dir) 3172521f2c2SPeter Oberparleiter target = kasprintf(GFP_KERNEL, "%s/%s.%s", dir, copy, ext); 3182521f2c2SPeter Oberparleiter else 3192521f2c2SPeter Oberparleiter target = kasprintf(GFP_KERNEL, "%s.%s", copy, ext); 3202521f2c2SPeter Oberparleiter kfree(copy); 3212521f2c2SPeter Oberparleiter 3222521f2c2SPeter Oberparleiter return target; 3232521f2c2SPeter Oberparleiter } 3242521f2c2SPeter Oberparleiter 3252521f2c2SPeter Oberparleiter /* 3262521f2c2SPeter Oberparleiter * Construct a string representing the symbolic link target for the given 3272521f2c2SPeter Oberparleiter * gcov data file name and link type. Depending on the link type and the 3282521f2c2SPeter Oberparleiter * location of the data file, the link target can either point to a 3292521f2c2SPeter Oberparleiter * subdirectory of srctree, objtree or in an external location. 3302521f2c2SPeter Oberparleiter */ 3312521f2c2SPeter Oberparleiter static char *get_link_target(const char *filename, const struct gcov_link *ext) 3322521f2c2SPeter Oberparleiter { 3332521f2c2SPeter Oberparleiter const char *rel; 3342521f2c2SPeter Oberparleiter char *result; 3352521f2c2SPeter Oberparleiter 3362521f2c2SPeter Oberparleiter if (strncmp(filename, objtree, strlen(objtree)) == 0) { 3372521f2c2SPeter Oberparleiter rel = filename + strlen(objtree) + 1; 3382521f2c2SPeter Oberparleiter if (ext->dir == SRC_TREE) 3392521f2c2SPeter Oberparleiter result = link_target(srctree, rel, ext->ext); 3402521f2c2SPeter Oberparleiter else 3412521f2c2SPeter Oberparleiter result = link_target(objtree, rel, ext->ext); 3422521f2c2SPeter Oberparleiter } else { 3432521f2c2SPeter Oberparleiter /* External compilation. */ 3442521f2c2SPeter Oberparleiter result = link_target(NULL, filename, ext->ext); 3452521f2c2SPeter Oberparleiter } 3462521f2c2SPeter Oberparleiter 3472521f2c2SPeter Oberparleiter return result; 3482521f2c2SPeter Oberparleiter } 3492521f2c2SPeter Oberparleiter 3502521f2c2SPeter Oberparleiter #define SKEW_PREFIX ".tmp_" 3512521f2c2SPeter Oberparleiter 3522521f2c2SPeter Oberparleiter /* 3532521f2c2SPeter Oberparleiter * For a filename .tmp_filename.ext return filename.ext. Needed to compensate 3542521f2c2SPeter Oberparleiter * for filename skewing caused by the mod-versioning mechanism. 3552521f2c2SPeter Oberparleiter */ 3562521f2c2SPeter Oberparleiter static const char *deskew(const char *basename) 3572521f2c2SPeter Oberparleiter { 3582521f2c2SPeter Oberparleiter if (strncmp(basename, SKEW_PREFIX, sizeof(SKEW_PREFIX) - 1) == 0) 3592521f2c2SPeter Oberparleiter return basename + sizeof(SKEW_PREFIX) - 1; 3602521f2c2SPeter Oberparleiter return basename; 3612521f2c2SPeter Oberparleiter } 3622521f2c2SPeter Oberparleiter 3632521f2c2SPeter Oberparleiter /* 3642521f2c2SPeter Oberparleiter * Create links to additional files (usually .c and .gcno files) which the 3652521f2c2SPeter Oberparleiter * gcov tool expects to find in the same directory as the gcov data file. 3662521f2c2SPeter Oberparleiter */ 3672521f2c2SPeter Oberparleiter static void add_links(struct gcov_node *node, struct dentry *parent) 3682521f2c2SPeter Oberparleiter { 3691931d433SAndy Shevchenko const char *basename; 3702521f2c2SPeter Oberparleiter char *target; 3712521f2c2SPeter Oberparleiter int num; 3722521f2c2SPeter Oberparleiter int i; 3732521f2c2SPeter Oberparleiter 3742521f2c2SPeter Oberparleiter for (num = 0; gcov_link[num].ext; num++) 3752521f2c2SPeter Oberparleiter /* Nothing. */; 3762521f2c2SPeter Oberparleiter node->links = kcalloc(num, sizeof(struct dentry *), GFP_KERNEL); 3772521f2c2SPeter Oberparleiter if (!node->links) 3782521f2c2SPeter Oberparleiter return; 3792521f2c2SPeter Oberparleiter for (i = 0; i < num; i++) { 3808cbce376SFrantisek Hrbata target = get_link_target( 3818cbce376SFrantisek Hrbata gcov_info_filename(get_node_info(node)), 3822521f2c2SPeter Oberparleiter &gcov_link[i]); 3832521f2c2SPeter Oberparleiter if (!target) 3842521f2c2SPeter Oberparleiter goto out_err; 3851931d433SAndy Shevchenko basename = kbasename(target); 3861931d433SAndy Shevchenko if (basename == target) 3872521f2c2SPeter Oberparleiter goto out_err; 3882521f2c2SPeter Oberparleiter node->links[i] = debugfs_create_symlink(deskew(basename), 3892521f2c2SPeter Oberparleiter parent, target); 3902521f2c2SPeter Oberparleiter if (!node->links[i]) 3912521f2c2SPeter Oberparleiter goto out_err; 3922521f2c2SPeter Oberparleiter kfree(target); 3932521f2c2SPeter Oberparleiter } 3942521f2c2SPeter Oberparleiter 3952521f2c2SPeter Oberparleiter return; 3962521f2c2SPeter Oberparleiter out_err: 3972521f2c2SPeter Oberparleiter kfree(target); 3982521f2c2SPeter Oberparleiter while (i-- > 0) 3992521f2c2SPeter Oberparleiter debugfs_remove(node->links[i]); 4002521f2c2SPeter Oberparleiter kfree(node->links); 4012521f2c2SPeter Oberparleiter node->links = NULL; 4022521f2c2SPeter Oberparleiter } 4032521f2c2SPeter Oberparleiter 4042521f2c2SPeter Oberparleiter static const struct file_operations gcov_data_fops = { 4052521f2c2SPeter Oberparleiter .open = gcov_seq_open, 4062521f2c2SPeter Oberparleiter .release = gcov_seq_release, 4072521f2c2SPeter Oberparleiter .read = seq_read, 4082521f2c2SPeter Oberparleiter .llseek = seq_lseek, 4092521f2c2SPeter Oberparleiter .write = gcov_seq_write, 4102521f2c2SPeter Oberparleiter }; 4112521f2c2SPeter Oberparleiter 4122521f2c2SPeter Oberparleiter /* Basic initialization of a new node. */ 4132521f2c2SPeter Oberparleiter static void init_node(struct gcov_node *node, struct gcov_info *info, 4142521f2c2SPeter Oberparleiter const char *name, struct gcov_node *parent) 4152521f2c2SPeter Oberparleiter { 4162521f2c2SPeter Oberparleiter INIT_LIST_HEAD(&node->list); 4172521f2c2SPeter Oberparleiter INIT_LIST_HEAD(&node->children); 4182521f2c2SPeter Oberparleiter INIT_LIST_HEAD(&node->all); 41985a0fdfdSPeter Oberparleiter if (node->loaded_info) { 42085a0fdfdSPeter Oberparleiter node->loaded_info[0] = info; 42185a0fdfdSPeter Oberparleiter node->num_loaded = 1; 42285a0fdfdSPeter Oberparleiter } 4232521f2c2SPeter Oberparleiter node->parent = parent; 4242521f2c2SPeter Oberparleiter if (name) 4252521f2c2SPeter Oberparleiter strcpy(node->name, name); 4262521f2c2SPeter Oberparleiter } 4272521f2c2SPeter Oberparleiter 4282521f2c2SPeter Oberparleiter /* 4292521f2c2SPeter Oberparleiter * Create a new node and associated debugfs entry. Needs to be called with 4302521f2c2SPeter Oberparleiter * node_lock held. 4312521f2c2SPeter Oberparleiter */ 4322521f2c2SPeter Oberparleiter static struct gcov_node *new_node(struct gcov_node *parent, 4332521f2c2SPeter Oberparleiter struct gcov_info *info, const char *name) 4342521f2c2SPeter Oberparleiter { 4352521f2c2SPeter Oberparleiter struct gcov_node *node; 4362521f2c2SPeter Oberparleiter 4372521f2c2SPeter Oberparleiter node = kzalloc(sizeof(struct gcov_node) + strlen(name) + 1, GFP_KERNEL); 43885a0fdfdSPeter Oberparleiter if (!node) 43985a0fdfdSPeter Oberparleiter goto err_nomem; 44085a0fdfdSPeter Oberparleiter if (info) { 44185a0fdfdSPeter Oberparleiter node->loaded_info = kcalloc(1, sizeof(struct gcov_info *), 44285a0fdfdSPeter Oberparleiter GFP_KERNEL); 44385a0fdfdSPeter Oberparleiter if (!node->loaded_info) 44485a0fdfdSPeter Oberparleiter goto err_nomem; 4452521f2c2SPeter Oberparleiter } 4462521f2c2SPeter Oberparleiter init_node(node, info, name, parent); 4472521f2c2SPeter Oberparleiter /* Differentiate between gcov data file nodes and directory nodes. */ 4482521f2c2SPeter Oberparleiter if (info) { 4492521f2c2SPeter Oberparleiter node->dentry = debugfs_create_file(deskew(node->name), 0600, 4502521f2c2SPeter Oberparleiter parent->dentry, node, &gcov_data_fops); 4512521f2c2SPeter Oberparleiter } else 4522521f2c2SPeter Oberparleiter node->dentry = debugfs_create_dir(node->name, parent->dentry); 4532521f2c2SPeter Oberparleiter if (!node->dentry) { 454a5ebb875SAndrew Morton pr_warn("could not create file\n"); 4552521f2c2SPeter Oberparleiter kfree(node); 4562521f2c2SPeter Oberparleiter return NULL; 4572521f2c2SPeter Oberparleiter } 4582521f2c2SPeter Oberparleiter if (info) 4592521f2c2SPeter Oberparleiter add_links(node, parent->dentry); 4602521f2c2SPeter Oberparleiter list_add(&node->list, &parent->children); 4612521f2c2SPeter Oberparleiter list_add(&node->all, &all_head); 4622521f2c2SPeter Oberparleiter 4632521f2c2SPeter Oberparleiter return node; 46485a0fdfdSPeter Oberparleiter 46585a0fdfdSPeter Oberparleiter err_nomem: 46685a0fdfdSPeter Oberparleiter kfree(node); 467a5ebb875SAndrew Morton pr_warn("out of memory\n"); 46885a0fdfdSPeter Oberparleiter return NULL; 4692521f2c2SPeter Oberparleiter } 4702521f2c2SPeter Oberparleiter 4712521f2c2SPeter Oberparleiter /* Remove symbolic links associated with node. */ 4722521f2c2SPeter Oberparleiter static void remove_links(struct gcov_node *node) 4732521f2c2SPeter Oberparleiter { 4742521f2c2SPeter Oberparleiter int i; 4752521f2c2SPeter Oberparleiter 4762521f2c2SPeter Oberparleiter if (!node->links) 4772521f2c2SPeter Oberparleiter return; 4782521f2c2SPeter Oberparleiter for (i = 0; gcov_link[i].ext; i++) 4792521f2c2SPeter Oberparleiter debugfs_remove(node->links[i]); 4802521f2c2SPeter Oberparleiter kfree(node->links); 4812521f2c2SPeter Oberparleiter node->links = NULL; 4822521f2c2SPeter Oberparleiter } 4832521f2c2SPeter Oberparleiter 4842521f2c2SPeter Oberparleiter /* 4852521f2c2SPeter Oberparleiter * Remove node from all lists and debugfs and release associated resources. 4862521f2c2SPeter Oberparleiter * Needs to be called with node_lock held. 4872521f2c2SPeter Oberparleiter */ 4882521f2c2SPeter Oberparleiter static void release_node(struct gcov_node *node) 4892521f2c2SPeter Oberparleiter { 4902521f2c2SPeter Oberparleiter list_del(&node->list); 4912521f2c2SPeter Oberparleiter list_del(&node->all); 4922521f2c2SPeter Oberparleiter debugfs_remove(node->dentry); 4932521f2c2SPeter Oberparleiter remove_links(node); 49485a0fdfdSPeter Oberparleiter kfree(node->loaded_info); 49585a0fdfdSPeter Oberparleiter if (node->unloaded_info) 49685a0fdfdSPeter Oberparleiter gcov_info_free(node->unloaded_info); 4972521f2c2SPeter Oberparleiter kfree(node); 4982521f2c2SPeter Oberparleiter } 4992521f2c2SPeter Oberparleiter 5002521f2c2SPeter Oberparleiter /* Release node and empty parents. Needs to be called with node_lock held. */ 5012521f2c2SPeter Oberparleiter static void remove_node(struct gcov_node *node) 5022521f2c2SPeter Oberparleiter { 5032521f2c2SPeter Oberparleiter struct gcov_node *parent; 5042521f2c2SPeter Oberparleiter 5052521f2c2SPeter Oberparleiter while ((node != &root_node) && list_empty(&node->children)) { 5062521f2c2SPeter Oberparleiter parent = node->parent; 5072521f2c2SPeter Oberparleiter release_node(node); 5082521f2c2SPeter Oberparleiter node = parent; 5092521f2c2SPeter Oberparleiter } 5102521f2c2SPeter Oberparleiter } 5112521f2c2SPeter Oberparleiter 5122521f2c2SPeter Oberparleiter /* 5132521f2c2SPeter Oberparleiter * Find child node with given basename. Needs to be called with node_lock 5142521f2c2SPeter Oberparleiter * held. 5152521f2c2SPeter Oberparleiter */ 5162521f2c2SPeter Oberparleiter static struct gcov_node *get_child_by_name(struct gcov_node *parent, 5172521f2c2SPeter Oberparleiter const char *name) 5182521f2c2SPeter Oberparleiter { 5192521f2c2SPeter Oberparleiter struct gcov_node *node; 5202521f2c2SPeter Oberparleiter 5212521f2c2SPeter Oberparleiter list_for_each_entry(node, &parent->children, list) { 5222521f2c2SPeter Oberparleiter if (strcmp(node->name, name) == 0) 5232521f2c2SPeter Oberparleiter return node; 5242521f2c2SPeter Oberparleiter } 5252521f2c2SPeter Oberparleiter 5262521f2c2SPeter Oberparleiter return NULL; 5272521f2c2SPeter Oberparleiter } 5282521f2c2SPeter Oberparleiter 5292521f2c2SPeter Oberparleiter /* 5302521f2c2SPeter Oberparleiter * write() implementation for reset file. Reset all profiling data to zero 53185a0fdfdSPeter Oberparleiter * and remove nodes for which all associated object files are unloaded. 5322521f2c2SPeter Oberparleiter */ 5332521f2c2SPeter Oberparleiter static ssize_t reset_write(struct file *file, const char __user *addr, 5342521f2c2SPeter Oberparleiter size_t len, loff_t *pos) 5352521f2c2SPeter Oberparleiter { 5362521f2c2SPeter Oberparleiter struct gcov_node *node; 5372521f2c2SPeter Oberparleiter 5382521f2c2SPeter Oberparleiter mutex_lock(&node_lock); 5392521f2c2SPeter Oberparleiter restart: 5402521f2c2SPeter Oberparleiter list_for_each_entry(node, &all_head, all) { 54185a0fdfdSPeter Oberparleiter if (node->num_loaded > 0) 54285a0fdfdSPeter Oberparleiter reset_node(node); 5432521f2c2SPeter Oberparleiter else if (list_empty(&node->children)) { 5442521f2c2SPeter Oberparleiter remove_node(node); 5452521f2c2SPeter Oberparleiter /* Several nodes may have gone - restart loop. */ 5462521f2c2SPeter Oberparleiter goto restart; 5472521f2c2SPeter Oberparleiter } 5482521f2c2SPeter Oberparleiter } 5492521f2c2SPeter Oberparleiter mutex_unlock(&node_lock); 5502521f2c2SPeter Oberparleiter 5512521f2c2SPeter Oberparleiter return len; 5522521f2c2SPeter Oberparleiter } 5532521f2c2SPeter Oberparleiter 5542521f2c2SPeter Oberparleiter /* read() implementation for reset file. Unused. */ 5552521f2c2SPeter Oberparleiter static ssize_t reset_read(struct file *file, char __user *addr, size_t len, 5562521f2c2SPeter Oberparleiter loff_t *pos) 5572521f2c2SPeter Oberparleiter { 5582521f2c2SPeter Oberparleiter /* Allow read operation so that a recursive copy won't fail. */ 5592521f2c2SPeter Oberparleiter return 0; 5602521f2c2SPeter Oberparleiter } 5612521f2c2SPeter Oberparleiter 5622521f2c2SPeter Oberparleiter static const struct file_operations gcov_reset_fops = { 5632521f2c2SPeter Oberparleiter .write = reset_write, 5642521f2c2SPeter Oberparleiter .read = reset_read, 5656038f373SArnd Bergmann .llseek = noop_llseek, 5662521f2c2SPeter Oberparleiter }; 5672521f2c2SPeter Oberparleiter 5682521f2c2SPeter Oberparleiter /* 5692521f2c2SPeter Oberparleiter * Create a node for a given profiling data set and add it to all lists and 5702521f2c2SPeter Oberparleiter * debugfs. Needs to be called with node_lock held. 5712521f2c2SPeter Oberparleiter */ 5722521f2c2SPeter Oberparleiter static void add_node(struct gcov_info *info) 5732521f2c2SPeter Oberparleiter { 5742521f2c2SPeter Oberparleiter char *filename; 5752521f2c2SPeter Oberparleiter char *curr; 5762521f2c2SPeter Oberparleiter char *next; 5772521f2c2SPeter Oberparleiter struct gcov_node *parent; 5782521f2c2SPeter Oberparleiter struct gcov_node *node; 5792521f2c2SPeter Oberparleiter 5808cbce376SFrantisek Hrbata filename = kstrdup(gcov_info_filename(info), GFP_KERNEL); 5812521f2c2SPeter Oberparleiter if (!filename) 5822521f2c2SPeter Oberparleiter return; 5832521f2c2SPeter Oberparleiter parent = &root_node; 5842521f2c2SPeter Oberparleiter /* Create directory nodes along the path. */ 5852521f2c2SPeter Oberparleiter for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) { 5862521f2c2SPeter Oberparleiter if (curr == next) 5872521f2c2SPeter Oberparleiter continue; 5882521f2c2SPeter Oberparleiter *next = 0; 5892521f2c2SPeter Oberparleiter if (strcmp(curr, ".") == 0) 5902521f2c2SPeter Oberparleiter continue; 5912521f2c2SPeter Oberparleiter if (strcmp(curr, "..") == 0) { 5922521f2c2SPeter Oberparleiter if (!parent->parent) 5932521f2c2SPeter Oberparleiter goto err_remove; 5942521f2c2SPeter Oberparleiter parent = parent->parent; 5952521f2c2SPeter Oberparleiter continue; 5962521f2c2SPeter Oberparleiter } 5972521f2c2SPeter Oberparleiter node = get_child_by_name(parent, curr); 5982521f2c2SPeter Oberparleiter if (!node) { 5992521f2c2SPeter Oberparleiter node = new_node(parent, NULL, curr); 6002521f2c2SPeter Oberparleiter if (!node) 6012521f2c2SPeter Oberparleiter goto err_remove; 6022521f2c2SPeter Oberparleiter } 6032521f2c2SPeter Oberparleiter parent = node; 6042521f2c2SPeter Oberparleiter } 6052521f2c2SPeter Oberparleiter /* Create file node. */ 6062521f2c2SPeter Oberparleiter node = new_node(parent, info, curr); 6072521f2c2SPeter Oberparleiter if (!node) 6082521f2c2SPeter Oberparleiter goto err_remove; 6092521f2c2SPeter Oberparleiter out: 6102521f2c2SPeter Oberparleiter kfree(filename); 6112521f2c2SPeter Oberparleiter return; 6122521f2c2SPeter Oberparleiter 6132521f2c2SPeter Oberparleiter err_remove: 6142521f2c2SPeter Oberparleiter remove_node(parent); 6152521f2c2SPeter Oberparleiter goto out; 6162521f2c2SPeter Oberparleiter } 6172521f2c2SPeter Oberparleiter 6182521f2c2SPeter Oberparleiter /* 61985a0fdfdSPeter Oberparleiter * Associate a profiling data set with an existing node. Needs to be called 62085a0fdfdSPeter Oberparleiter * with node_lock held. 6212521f2c2SPeter Oberparleiter */ 62285a0fdfdSPeter Oberparleiter static void add_info(struct gcov_node *node, struct gcov_info *info) 6232521f2c2SPeter Oberparleiter { 62485a0fdfdSPeter Oberparleiter struct gcov_info **loaded_info; 62585a0fdfdSPeter Oberparleiter int num = node->num_loaded; 6262521f2c2SPeter Oberparleiter 62785a0fdfdSPeter Oberparleiter /* 62885a0fdfdSPeter Oberparleiter * Prepare new array. This is done first to simplify cleanup in 62985a0fdfdSPeter Oberparleiter * case the new data set is incompatible, the node only contains 63085a0fdfdSPeter Oberparleiter * unloaded data sets and there's not enough memory for the array. 63185a0fdfdSPeter Oberparleiter */ 63285a0fdfdSPeter Oberparleiter loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL); 63385a0fdfdSPeter Oberparleiter if (!loaded_info) { 634a5ebb875SAndrew Morton pr_warn("could not add '%s' (out of memory)\n", 6358cbce376SFrantisek Hrbata gcov_info_filename(info)); 63685a0fdfdSPeter Oberparleiter return; 63785a0fdfdSPeter Oberparleiter } 63885a0fdfdSPeter Oberparleiter memcpy(loaded_info, node->loaded_info, 63985a0fdfdSPeter Oberparleiter num * sizeof(struct gcov_info *)); 64085a0fdfdSPeter Oberparleiter loaded_info[num] = info; 64185a0fdfdSPeter Oberparleiter /* Check if the new data set is compatible. */ 64285a0fdfdSPeter Oberparleiter if (num == 0) { 64385a0fdfdSPeter Oberparleiter /* 64485a0fdfdSPeter Oberparleiter * A module was unloaded, modified and reloaded. The new 64585a0fdfdSPeter Oberparleiter * data set replaces the copy of the last one. 64685a0fdfdSPeter Oberparleiter */ 64785a0fdfdSPeter Oberparleiter if (!gcov_info_is_compatible(node->unloaded_info, info)) { 648a5ebb875SAndrew Morton pr_warn("discarding saved data for %s " 6498cbce376SFrantisek Hrbata "(incompatible version)\n", 6508cbce376SFrantisek Hrbata gcov_info_filename(info)); 65185a0fdfdSPeter Oberparleiter gcov_info_free(node->unloaded_info); 65285a0fdfdSPeter Oberparleiter node->unloaded_info = NULL; 65385a0fdfdSPeter Oberparleiter } 65485a0fdfdSPeter Oberparleiter } else { 65585a0fdfdSPeter Oberparleiter /* 65685a0fdfdSPeter Oberparleiter * Two different versions of the same object file are loaded. 65785a0fdfdSPeter Oberparleiter * The initial one takes precedence. 65885a0fdfdSPeter Oberparleiter */ 65985a0fdfdSPeter Oberparleiter if (!gcov_info_is_compatible(node->loaded_info[0], info)) { 660a5ebb875SAndrew Morton pr_warn("could not add '%s' (incompatible " 6618cbce376SFrantisek Hrbata "version)\n", gcov_info_filename(info)); 66285a0fdfdSPeter Oberparleiter kfree(loaded_info); 66385a0fdfdSPeter Oberparleiter return; 66485a0fdfdSPeter Oberparleiter } 66585a0fdfdSPeter Oberparleiter } 66685a0fdfdSPeter Oberparleiter /* Overwrite previous array. */ 66785a0fdfdSPeter Oberparleiter kfree(node->loaded_info); 66885a0fdfdSPeter Oberparleiter node->loaded_info = loaded_info; 66985a0fdfdSPeter Oberparleiter node->num_loaded = num + 1; 6702521f2c2SPeter Oberparleiter } 6712521f2c2SPeter Oberparleiter 6722521f2c2SPeter Oberparleiter /* 67385a0fdfdSPeter Oberparleiter * Return the index of a profiling data set associated with a node. 6742521f2c2SPeter Oberparleiter */ 67585a0fdfdSPeter Oberparleiter static int get_info_index(struct gcov_node *node, struct gcov_info *info) 6762521f2c2SPeter Oberparleiter { 67785a0fdfdSPeter Oberparleiter int i; 67885a0fdfdSPeter Oberparleiter 67985a0fdfdSPeter Oberparleiter for (i = 0; i < node->num_loaded; i++) { 68085a0fdfdSPeter Oberparleiter if (node->loaded_info[i] == info) 68185a0fdfdSPeter Oberparleiter return i; 6822521f2c2SPeter Oberparleiter } 68385a0fdfdSPeter Oberparleiter return -ENOENT; 68485a0fdfdSPeter Oberparleiter } 68585a0fdfdSPeter Oberparleiter 68685a0fdfdSPeter Oberparleiter /* 68785a0fdfdSPeter Oberparleiter * Save the data of a profiling data set which is being unloaded. 68885a0fdfdSPeter Oberparleiter */ 68985a0fdfdSPeter Oberparleiter static void save_info(struct gcov_node *node, struct gcov_info *info) 69085a0fdfdSPeter Oberparleiter { 69185a0fdfdSPeter Oberparleiter if (node->unloaded_info) 69285a0fdfdSPeter Oberparleiter gcov_info_add(node->unloaded_info, info); 69385a0fdfdSPeter Oberparleiter else { 69485a0fdfdSPeter Oberparleiter node->unloaded_info = gcov_info_dup(info); 69585a0fdfdSPeter Oberparleiter if (!node->unloaded_info) { 696a5ebb875SAndrew Morton pr_warn("could not save data for '%s' " 6978cbce376SFrantisek Hrbata "(out of memory)\n", 6988cbce376SFrantisek Hrbata gcov_info_filename(info)); 69985a0fdfdSPeter Oberparleiter } 70085a0fdfdSPeter Oberparleiter } 70185a0fdfdSPeter Oberparleiter } 70285a0fdfdSPeter Oberparleiter 70385a0fdfdSPeter Oberparleiter /* 70485a0fdfdSPeter Oberparleiter * Disassociate a profiling data set from a node. Needs to be called with 70585a0fdfdSPeter Oberparleiter * node_lock held. 70685a0fdfdSPeter Oberparleiter */ 70785a0fdfdSPeter Oberparleiter static void remove_info(struct gcov_node *node, struct gcov_info *info) 70885a0fdfdSPeter Oberparleiter { 70985a0fdfdSPeter Oberparleiter int i; 71085a0fdfdSPeter Oberparleiter 71185a0fdfdSPeter Oberparleiter i = get_info_index(node, info); 71285a0fdfdSPeter Oberparleiter if (i < 0) { 713a5ebb875SAndrew Morton pr_warn("could not remove '%s' (not found)\n", 7148cbce376SFrantisek Hrbata gcov_info_filename(info)); 71585a0fdfdSPeter Oberparleiter return; 71685a0fdfdSPeter Oberparleiter } 71785a0fdfdSPeter Oberparleiter if (gcov_persist) 71885a0fdfdSPeter Oberparleiter save_info(node, info); 71985a0fdfdSPeter Oberparleiter /* Shrink array. */ 72085a0fdfdSPeter Oberparleiter node->loaded_info[i] = node->loaded_info[node->num_loaded - 1]; 72185a0fdfdSPeter Oberparleiter node->num_loaded--; 72285a0fdfdSPeter Oberparleiter if (node->num_loaded > 0) 72385a0fdfdSPeter Oberparleiter return; 72485a0fdfdSPeter Oberparleiter /* Last loaded data set was removed. */ 72585a0fdfdSPeter Oberparleiter kfree(node->loaded_info); 72685a0fdfdSPeter Oberparleiter node->loaded_info = NULL; 72785a0fdfdSPeter Oberparleiter node->num_loaded = 0; 72885a0fdfdSPeter Oberparleiter if (!node->unloaded_info) 72985a0fdfdSPeter Oberparleiter remove_node(node); 7302521f2c2SPeter Oberparleiter } 7312521f2c2SPeter Oberparleiter 7322521f2c2SPeter Oberparleiter /* 7332521f2c2SPeter Oberparleiter * Callback to create/remove profiling files when code compiled with 7342521f2c2SPeter Oberparleiter * -fprofile-arcs is loaded/unloaded. 7352521f2c2SPeter Oberparleiter */ 7362521f2c2SPeter Oberparleiter void gcov_event(enum gcov_action action, struct gcov_info *info) 7372521f2c2SPeter Oberparleiter { 7382521f2c2SPeter Oberparleiter struct gcov_node *node; 7392521f2c2SPeter Oberparleiter 7402521f2c2SPeter Oberparleiter mutex_lock(&node_lock); 7418cbce376SFrantisek Hrbata node = get_node_by_name(gcov_info_filename(info)); 7422521f2c2SPeter Oberparleiter switch (action) { 7432521f2c2SPeter Oberparleiter case GCOV_ADD: 74485a0fdfdSPeter Oberparleiter if (node) 74585a0fdfdSPeter Oberparleiter add_info(node, info); 74685a0fdfdSPeter Oberparleiter else 7472521f2c2SPeter Oberparleiter add_node(info); 7482521f2c2SPeter Oberparleiter break; 7492521f2c2SPeter Oberparleiter case GCOV_REMOVE: 75085a0fdfdSPeter Oberparleiter if (node) 75185a0fdfdSPeter Oberparleiter remove_info(node, info); 75285a0fdfdSPeter Oberparleiter else { 753a5ebb875SAndrew Morton pr_warn("could not remove '%s' (not found)\n", 7548cbce376SFrantisek Hrbata gcov_info_filename(info)); 7552521f2c2SPeter Oberparleiter } 7562521f2c2SPeter Oberparleiter break; 7572521f2c2SPeter Oberparleiter } 7582521f2c2SPeter Oberparleiter mutex_unlock(&node_lock); 7592521f2c2SPeter Oberparleiter } 7602521f2c2SPeter Oberparleiter 7612521f2c2SPeter Oberparleiter /* Create debugfs entries. */ 7622521f2c2SPeter Oberparleiter static __init int gcov_fs_init(void) 7632521f2c2SPeter Oberparleiter { 7642521f2c2SPeter Oberparleiter int rc = -EIO; 7652521f2c2SPeter Oberparleiter 7662521f2c2SPeter Oberparleiter init_node(&root_node, NULL, NULL, NULL); 7672521f2c2SPeter Oberparleiter /* 7682521f2c2SPeter Oberparleiter * /sys/kernel/debug/gcov will be parent for the reset control file 7692521f2c2SPeter Oberparleiter * and all profiling files. 7702521f2c2SPeter Oberparleiter */ 7712521f2c2SPeter Oberparleiter root_node.dentry = debugfs_create_dir("gcov", NULL); 7722521f2c2SPeter Oberparleiter if (!root_node.dentry) 7732521f2c2SPeter Oberparleiter goto err_remove; 7742521f2c2SPeter Oberparleiter /* 7752521f2c2SPeter Oberparleiter * Create reset file which resets all profiling counts when written 7762521f2c2SPeter Oberparleiter * to. 7772521f2c2SPeter Oberparleiter */ 7782521f2c2SPeter Oberparleiter reset_dentry = debugfs_create_file("reset", 0600, root_node.dentry, 7792521f2c2SPeter Oberparleiter NULL, &gcov_reset_fops); 7802521f2c2SPeter Oberparleiter if (!reset_dentry) 7812521f2c2SPeter Oberparleiter goto err_remove; 7822521f2c2SPeter Oberparleiter /* Replay previous events to get our fs hierarchy up-to-date. */ 7832521f2c2SPeter Oberparleiter gcov_enable_events(); 7842521f2c2SPeter Oberparleiter return 0; 7852521f2c2SPeter Oberparleiter 7862521f2c2SPeter Oberparleiter err_remove: 7872521f2c2SPeter Oberparleiter pr_err("init failed\n"); 7882521f2c2SPeter Oberparleiter debugfs_remove(root_node.dentry); 7892521f2c2SPeter Oberparleiter 7902521f2c2SPeter Oberparleiter return rc; 7912521f2c2SPeter Oberparleiter } 7922521f2c2SPeter Oberparleiter device_initcall(gcov_fs_init); 793