1 // SPDX-License-Identifier: GPL-2.0
3 * This code exports profiling data as debugfs files to userspace.
5 * Copyright IBM Corp. 2009
6 * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
8 * Uses gcc-internal data definitions.
9 * Based on the gcov-kernel patch by:
10 * Hubertus Franke <frankeh@us.ibm.com>
11 * Nigel Hinds <nhinds@us.ibm.com>
12 * Rajan Ravindran <rajancr@us.ibm.com>
13 * Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
18 #define pr_fmt(fmt) "gcov: " fmt
20 #include <linux/init.h>
21 #include <linux/module.h>
22 #include <linux/debugfs.h>
24 #include <linux/list.h>
25 #include <linux/string.h>
26 #include <linux/slab.h>
27 #include <linux/mutex.h>
28 #include <linux/seq_file.h>
32 * struct gcov_node - represents a debugfs entry
33 * @list: list head for child node list
34 * @children: child nodes
35 * @all: list head for list of all nodes
36 * @parent: parent node
37 * @loaded_info: array of pointers to profiling data sets for loaded object
39 * @num_loaded: number of profiling data sets for loaded object files.
40 * @unloaded_info: accumulated copy of profiling data sets for unloaded
41 * object files. Used only when gcov_persist=1.
42 * @dentry: main debugfs entry, either a directory or data file
43 * @links: associated symbolic links
44 * @name: data file basename
46 * struct gcov_node represents an entity within the gcov/ subdirectory
47 * of debugfs. There are directory and data file nodes. The latter represent
48 * the actual synthesized data file plus any associated symbolic links which
49 * are needed by the gcov tool to work correctly.
52 struct list_head list
;
53 struct list_head children
;
55 struct gcov_node
*parent
;
56 struct gcov_info
**loaded_info
;
57 struct gcov_info
*unloaded_info
;
58 struct dentry
*dentry
;
59 struct dentry
**links
;
64 static const char objtree
[] = OBJTREE
;
65 static const char srctree
[] = SRCTREE
;
66 static struct gcov_node root_node
;
67 static struct dentry
*reset_dentry
;
68 static LIST_HEAD(all_head
);
69 static DEFINE_MUTEX(node_lock
);
71 /* If non-zero, keep copies of profiling data for unloaded modules. */
72 static int gcov_persist
= 1;
74 static int __init
gcov_persist_setup(char *str
)
78 if (kstrtoul(str
, 0, &val
)) {
79 pr_warn("invalid gcov_persist parameter '%s'\n", str
);
83 pr_info("setting gcov_persist to %d\n", gcov_persist
);
87 __setup("gcov_persist=", gcov_persist_setup
);
90 * seq_file.start() implementation for gcov data files. Note that the
91 * gcov_iterator interface is designed to be more restrictive than seq_file
92 * (no start from arbitrary position, etc.), to simplify the iterator
95 static void *gcov_seq_start(struct seq_file
*seq
, loff_t
*pos
)
99 gcov_iter_start(seq
->private);
100 for (i
= 0; i
< *pos
; i
++) {
101 if (gcov_iter_next(seq
->private))
107 /* seq_file.next() implementation for gcov data files. */
108 static void *gcov_seq_next(struct seq_file
*seq
, void *data
, loff_t
*pos
)
110 struct gcov_iterator
*iter
= data
;
112 if (gcov_iter_next(iter
))
119 /* seq_file.show() implementation for gcov data files. */
120 static int gcov_seq_show(struct seq_file
*seq
, void *data
)
122 struct gcov_iterator
*iter
= data
;
124 if (gcov_iter_write(iter
, seq
))
129 static void gcov_seq_stop(struct seq_file
*seq
, void *data
)
134 static const struct seq_operations gcov_seq_ops
= {
135 .start
= gcov_seq_start
,
136 .next
= gcov_seq_next
,
137 .show
= gcov_seq_show
,
138 .stop
= gcov_seq_stop
,
142 * Return a profiling data set associated with the given node. This is
143 * either a data set for a loaded object file or a data set copy in case
144 * all associated object files have been unloaded.
146 static struct gcov_info
*get_node_info(struct gcov_node
*node
)
148 if (node
->num_loaded
> 0)
149 return node
->loaded_info
[0];
151 return node
->unloaded_info
;
155 * Return a newly allocated profiling data set which contains the sum of
156 * all profiling data associated with the given node.
158 static struct gcov_info
*get_accumulated_info(struct gcov_node
*node
)
160 struct gcov_info
*info
;
163 if (node
->unloaded_info
)
164 info
= gcov_info_dup(node
->unloaded_info
);
166 info
= gcov_info_dup(node
->loaded_info
[i
++]);
169 for (; i
< node
->num_loaded
; i
++)
170 gcov_info_add(info
, node
->loaded_info
[i
]);
176 * open() implementation for gcov data files. Create a copy of the profiling
177 * data set and initialize the iterator and seq_file interface.
179 static int gcov_seq_open(struct inode
*inode
, struct file
*file
)
181 struct gcov_node
*node
= inode
->i_private
;
182 struct gcov_iterator
*iter
;
183 struct seq_file
*seq
;
184 struct gcov_info
*info
;
187 mutex_lock(&node_lock
);
189 * Read from a profiling data copy to minimize reference tracking
190 * complexity and concurrent access and to keep accumulating multiple
191 * profiling data sets associated with one node simple.
193 info
= get_accumulated_info(node
);
196 iter
= gcov_iter_new(info
);
199 rc
= seq_open(file
, &gcov_seq_ops
);
201 goto err_free_iter_info
;
202 seq
= file
->private_data
;
205 mutex_unlock(&node_lock
);
209 gcov_iter_free(iter
);
211 gcov_info_free(info
);
216 * release() implementation for gcov data files. Release resources allocated
219 static int gcov_seq_release(struct inode
*inode
, struct file
*file
)
221 struct gcov_iterator
*iter
;
222 struct gcov_info
*info
;
223 struct seq_file
*seq
;
225 seq
= file
->private_data
;
227 info
= gcov_iter_get_info(iter
);
228 gcov_iter_free(iter
);
229 gcov_info_free(info
);
230 seq_release(inode
, file
);
236 * Find a node by the associated data file name. Needs to be called with
239 static struct gcov_node
*get_node_by_name(const char *name
)
241 struct gcov_node
*node
;
242 struct gcov_info
*info
;
244 list_for_each_entry(node
, &all_head
, all
) {
245 info
= get_node_info(node
);
246 if (info
&& (strcmp(gcov_info_filename(info
), name
) == 0))
254 * Reset all profiling data associated with the specified node.
256 static void reset_node(struct gcov_node
*node
)
260 if (node
->unloaded_info
)
261 gcov_info_reset(node
->unloaded_info
);
262 for (i
= 0; i
< node
->num_loaded
; i
++)
263 gcov_info_reset(node
->loaded_info
[i
]);
266 static void remove_node(struct gcov_node
*node
);
269 * write() implementation for gcov data files. Reset profiling data for the
270 * corresponding file. If all associated object files have been unloaded,
271 * remove the debug fs node as well.
273 static ssize_t
gcov_seq_write(struct file
*file
, const char __user
*addr
,
274 size_t len
, loff_t
*pos
)
276 struct seq_file
*seq
;
277 struct gcov_info
*info
;
278 struct gcov_node
*node
;
280 seq
= file
->private_data
;
281 info
= gcov_iter_get_info(seq
->private);
282 mutex_lock(&node_lock
);
283 node
= get_node_by_name(gcov_info_filename(info
));
285 /* Reset counts or remove node for unloaded modules. */
286 if (node
->num_loaded
== 0)
291 /* Reset counts for open file. */
292 gcov_info_reset(info
);
293 mutex_unlock(&node_lock
);
299 * Given a string <path> representing a file path of format:
301 * construct and return a new string:
302 * <dir/>path/to/file.<ext>
304 static char *link_target(const char *dir
, const char *path
, const char *ext
)
310 copy
= kstrdup(path
, GFP_KERNEL
);
313 old_ext
= strrchr(copy
, '.');
317 target
= kasprintf(GFP_KERNEL
, "%s/%s.%s", dir
, copy
, ext
);
319 target
= kasprintf(GFP_KERNEL
, "%s.%s", copy
, ext
);
326 * Construct a string representing the symbolic link target for the given
327 * gcov data file name and link type. Depending on the link type and the
328 * location of the data file, the link target can either point to a
329 * subdirectory of srctree, objtree or in an external location.
331 static char *get_link_target(const char *filename
, const struct gcov_link
*ext
)
336 if (strncmp(filename
, objtree
, strlen(objtree
)) == 0) {
337 rel
= filename
+ strlen(objtree
) + 1;
338 if (ext
->dir
== SRC_TREE
)
339 result
= link_target(srctree
, rel
, ext
->ext
);
341 result
= link_target(objtree
, rel
, ext
->ext
);
343 /* External compilation. */
344 result
= link_target(NULL
, filename
, ext
->ext
);
350 #define SKEW_PREFIX ".tmp_"
353 * For a filename .tmp_filename.ext return filename.ext. Needed to compensate
354 * for filename skewing caused by the mod-versioning mechanism.
356 static const char *deskew(const char *basename
)
358 if (strncmp(basename
, SKEW_PREFIX
, sizeof(SKEW_PREFIX
) - 1) == 0)
359 return basename
+ sizeof(SKEW_PREFIX
) - 1;
364 * Create links to additional files (usually .c and .gcno files) which the
365 * gcov tool expects to find in the same directory as the gcov data file.
367 static void add_links(struct gcov_node
*node
, struct dentry
*parent
)
369 const char *basename
;
374 for (num
= 0; gcov_link
[num
].ext
; num
++)
376 node
->links
= kcalloc(num
, sizeof(struct dentry
*), GFP_KERNEL
);
379 for (i
= 0; i
< num
; i
++) {
380 target
= get_link_target(
381 gcov_info_filename(get_node_info(node
)),
385 basename
= kbasename(target
);
386 if (basename
== target
)
388 node
->links
[i
] = debugfs_create_symlink(deskew(basename
),
399 debugfs_remove(node
->links
[i
]);
404 static const struct file_operations gcov_data_fops
= {
405 .open
= gcov_seq_open
,
406 .release
= gcov_seq_release
,
409 .write
= gcov_seq_write
,
412 /* Basic initialization of a new node. */
413 static void init_node(struct gcov_node
*node
, struct gcov_info
*info
,
414 const char *name
, struct gcov_node
*parent
)
416 INIT_LIST_HEAD(&node
->list
);
417 INIT_LIST_HEAD(&node
->children
);
418 INIT_LIST_HEAD(&node
->all
);
419 if (node
->loaded_info
) {
420 node
->loaded_info
[0] = info
;
421 node
->num_loaded
= 1;
423 node
->parent
= parent
;
425 strcpy(node
->name
, name
);
429 * Create a new node and associated debugfs entry. Needs to be called with
432 static struct gcov_node
*new_node(struct gcov_node
*parent
,
433 struct gcov_info
*info
, const char *name
)
435 struct gcov_node
*node
;
437 node
= kzalloc(sizeof(struct gcov_node
) + strlen(name
) + 1, GFP_KERNEL
);
441 node
->loaded_info
= kcalloc(1, sizeof(struct gcov_info
*),
443 if (!node
->loaded_info
)
446 init_node(node
, info
, name
, parent
);
447 /* Differentiate between gcov data file nodes and directory nodes. */
449 node
->dentry
= debugfs_create_file(deskew(node
->name
), 0600,
450 parent
->dentry
, node
, &gcov_data_fops
);
452 node
->dentry
= debugfs_create_dir(node
->name
, parent
->dentry
);
454 pr_warn("could not create file\n");
459 add_links(node
, parent
->dentry
);
460 list_add(&node
->list
, &parent
->children
);
461 list_add(&node
->all
, &all_head
);
467 pr_warn("out of memory\n");
471 /* Remove symbolic links associated with node. */
472 static void remove_links(struct gcov_node
*node
)
478 for (i
= 0; gcov_link
[i
].ext
; i
++)
479 debugfs_remove(node
->links
[i
]);
485 * Remove node from all lists and debugfs and release associated resources.
486 * Needs to be called with node_lock held.
488 static void release_node(struct gcov_node
*node
)
490 list_del(&node
->list
);
491 list_del(&node
->all
);
492 debugfs_remove(node
->dentry
);
494 kfree(node
->loaded_info
);
495 if (node
->unloaded_info
)
496 gcov_info_free(node
->unloaded_info
);
500 /* Release node and empty parents. Needs to be called with node_lock held. */
501 static void remove_node(struct gcov_node
*node
)
503 struct gcov_node
*parent
;
505 while ((node
!= &root_node
) && list_empty(&node
->children
)) {
506 parent
= node
->parent
;
513 * Find child node with given basename. Needs to be called with node_lock
516 static struct gcov_node
*get_child_by_name(struct gcov_node
*parent
,
519 struct gcov_node
*node
;
521 list_for_each_entry(node
, &parent
->children
, list
) {
522 if (strcmp(node
->name
, name
) == 0)
530 * write() implementation for reset file. Reset all profiling data to zero
531 * and remove nodes for which all associated object files are unloaded.
533 static ssize_t
reset_write(struct file
*file
, const char __user
*addr
,
534 size_t len
, loff_t
*pos
)
536 struct gcov_node
*node
;
538 mutex_lock(&node_lock
);
540 list_for_each_entry(node
, &all_head
, all
) {
541 if (node
->num_loaded
> 0)
543 else if (list_empty(&node
->children
)) {
545 /* Several nodes may have gone - restart loop. */
549 mutex_unlock(&node_lock
);
554 /* read() implementation for reset file. Unused. */
555 static ssize_t
reset_read(struct file
*file
, char __user
*addr
, size_t len
,
558 /* Allow read operation so that a recursive copy won't fail. */
562 static const struct file_operations gcov_reset_fops
= {
563 .write
= reset_write
,
565 .llseek
= noop_llseek
,
569 * Create a node for a given profiling data set and add it to all lists and
570 * debugfs. Needs to be called with node_lock held.
572 static void add_node(struct gcov_info
*info
)
577 struct gcov_node
*parent
;
578 struct gcov_node
*node
;
580 filename
= kstrdup(gcov_info_filename(info
), GFP_KERNEL
);
584 /* Create directory nodes along the path. */
585 for (curr
= filename
; (next
= strchr(curr
, '/')); curr
= next
+ 1) {
589 if (strcmp(curr
, ".") == 0)
591 if (strcmp(curr
, "..") == 0) {
594 parent
= parent
->parent
;
597 node
= get_child_by_name(parent
, curr
);
599 node
= new_node(parent
, NULL
, curr
);
605 /* Create file node. */
606 node
= new_node(parent
, info
, curr
);
619 * Associate a profiling data set with an existing node. Needs to be called
620 * with node_lock held.
622 static void add_info(struct gcov_node
*node
, struct gcov_info
*info
)
624 struct gcov_info
**loaded_info
;
625 int num
= node
->num_loaded
;
628 * Prepare new array. This is done first to simplify cleanup in
629 * case the new data set is incompatible, the node only contains
630 * unloaded data sets and there's not enough memory for the array.
632 loaded_info
= kcalloc(num
+ 1, sizeof(struct gcov_info
*), GFP_KERNEL
);
634 pr_warn("could not add '%s' (out of memory)\n",
635 gcov_info_filename(info
));
638 memcpy(loaded_info
, node
->loaded_info
,
639 num
* sizeof(struct gcov_info
*));
640 loaded_info
[num
] = info
;
641 /* Check if the new data set is compatible. */
644 * A module was unloaded, modified and reloaded. The new
645 * data set replaces the copy of the last one.
647 if (!gcov_info_is_compatible(node
->unloaded_info
, info
)) {
648 pr_warn("discarding saved data for %s "
649 "(incompatible version)\n",
650 gcov_info_filename(info
));
651 gcov_info_free(node
->unloaded_info
);
652 node
->unloaded_info
= NULL
;
656 * Two different versions of the same object file are loaded.
657 * The initial one takes precedence.
659 if (!gcov_info_is_compatible(node
->loaded_info
[0], info
)) {
660 pr_warn("could not add '%s' (incompatible "
661 "version)\n", gcov_info_filename(info
));
666 /* Overwrite previous array. */
667 kfree(node
->loaded_info
);
668 node
->loaded_info
= loaded_info
;
669 node
->num_loaded
= num
+ 1;
673 * Return the index of a profiling data set associated with a node.
675 static int get_info_index(struct gcov_node
*node
, struct gcov_info
*info
)
679 for (i
= 0; i
< node
->num_loaded
; i
++) {
680 if (node
->loaded_info
[i
] == info
)
687 * Save the data of a profiling data set which is being unloaded.
689 static void save_info(struct gcov_node
*node
, struct gcov_info
*info
)
691 if (node
->unloaded_info
)
692 gcov_info_add(node
->unloaded_info
, info
);
694 node
->unloaded_info
= gcov_info_dup(info
);
695 if (!node
->unloaded_info
) {
696 pr_warn("could not save data for '%s' "
698 gcov_info_filename(info
));
704 * Disassociate a profiling data set from a node. Needs to be called with
707 static void remove_info(struct gcov_node
*node
, struct gcov_info
*info
)
711 i
= get_info_index(node
, info
);
713 pr_warn("could not remove '%s' (not found)\n",
714 gcov_info_filename(info
));
718 save_info(node
, info
);
720 node
->loaded_info
[i
] = node
->loaded_info
[node
->num_loaded
- 1];
722 if (node
->num_loaded
> 0)
724 /* Last loaded data set was removed. */
725 kfree(node
->loaded_info
);
726 node
->loaded_info
= NULL
;
727 node
->num_loaded
= 0;
728 if (!node
->unloaded_info
)
733 * Callback to create/remove profiling files when code compiled with
734 * -fprofile-arcs is loaded/unloaded.
736 void gcov_event(enum gcov_action action
, struct gcov_info
*info
)
738 struct gcov_node
*node
;
740 mutex_lock(&node_lock
);
741 node
= get_node_by_name(gcov_info_filename(info
));
745 add_info(node
, info
);
751 remove_info(node
, info
);
753 pr_warn("could not remove '%s' (not found)\n",
754 gcov_info_filename(info
));
758 mutex_unlock(&node_lock
);
761 /* Create debugfs entries. */
762 static __init
int gcov_fs_init(void)
766 init_node(&root_node
, NULL
, NULL
, NULL
);
768 * /sys/kernel/debug/gcov will be parent for the reset control file
769 * and all profiling files.
771 root_node
.dentry
= debugfs_create_dir("gcov", NULL
);
772 if (!root_node
.dentry
)
775 * Create reset file which resets all profiling counts when written
778 reset_dentry
= debugfs_create_file("reset", 0600, root_node
.dentry
,
779 NULL
, &gcov_reset_fops
);
782 /* Replay previous events to get our fs hierarchy up-to-date. */
783 gcov_enable_events();
787 pr_err("init failed\n");
788 debugfs_remove(root_node
.dentry
);
792 device_initcall(gcov_fs_init
);