1 /* 2 * This code exports profiling data as debugfs files to userspace. 3 * 4 * Copyright IBM Corp. 2009 5 * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com> 6 * 7 * Uses gcc-internal data definitions. 8 * Based on the gcov-kernel patch by: 9 * Hubertus Franke <frankeh@us.ibm.com> 10 * Nigel Hinds <nhinds@us.ibm.com> 11 * Rajan Ravindran <rajancr@us.ibm.com> 12 * Peter Oberparleiter <oberpar@linux.vnet.ibm.com> 13 * Paul Larson 14 * Yi CDL Yang 15 */ 16 17 #define pr_fmt(fmt) "gcov: " fmt 18 19 #include <linux/init.h> 20 #include <linux/module.h> 21 #include <linux/debugfs.h> 22 #include <linux/fs.h> 23 #include <linux/list.h> 24 #include <linux/string.h> 25 #include <linux/slab.h> 26 #include <linux/mutex.h> 27 #include <linux/seq_file.h> 28 #include "gcov.h" 29 30 /** 31 * struct gcov_node - represents a debugfs entry 32 * @list: list head for child node list 33 * @children: child nodes 34 * @all: list head for list of all nodes 35 * @parent: parent node 36 * @info: associated profiling data structure if not a directory 37 * @ghost: when an object file containing profiling data is unloaded we keep a 38 * copy of the profiling data here to allow collecting coverage data 39 * for cleanup code. Such a node is called a "ghost". 40 * @dentry: main debugfs entry, either a directory or data file 41 * @links: associated symbolic links 42 * @name: data file basename 43 * 44 * struct gcov_node represents an entity within the gcov/ subdirectory 45 * of debugfs. There are directory and data file nodes. The latter represent 46 * the actual synthesized data file plus any associated symbolic links which 47 * are needed by the gcov tool to work correctly. 48 */ 49 struct gcov_node { 50 struct list_head list; 51 struct list_head children; 52 struct list_head all; 53 struct gcov_node *parent; 54 struct gcov_info *info; 55 struct gcov_info *ghost; 56 struct dentry *dentry; 57 struct dentry **links; 58 char name[0]; 59 }; 60 61 static const char objtree[] = OBJTREE; 62 static const char srctree[] = SRCTREE; 63 static struct gcov_node root_node; 64 static struct dentry *reset_dentry; 65 static LIST_HEAD(all_head); 66 static DEFINE_MUTEX(node_lock); 67 68 /* If non-zero, keep copies of profiling data for unloaded modules. */ 69 static int gcov_persist = 1; 70 71 static int __init gcov_persist_setup(char *str) 72 { 73 unsigned long val; 74 75 if (strict_strtoul(str, 0, &val)) { 76 pr_warning("invalid gcov_persist parameter '%s'\n", str); 77 return 0; 78 } 79 gcov_persist = val; 80 pr_info("setting gcov_persist to %d\n", gcov_persist); 81 82 return 1; 83 } 84 __setup("gcov_persist=", gcov_persist_setup); 85 86 /* 87 * seq_file.start() implementation for gcov data files. Note that the 88 * gcov_iterator interface is designed to be more restrictive than seq_file 89 * (no start from arbitrary position, etc.), to simplify the iterator 90 * implementation. 91 */ 92 static void *gcov_seq_start(struct seq_file *seq, loff_t *pos) 93 { 94 loff_t i; 95 96 gcov_iter_start(seq->private); 97 for (i = 0; i < *pos; i++) { 98 if (gcov_iter_next(seq->private)) 99 return NULL; 100 } 101 return seq->private; 102 } 103 104 /* seq_file.next() implementation for gcov data files. */ 105 static void *gcov_seq_next(struct seq_file *seq, void *data, loff_t *pos) 106 { 107 struct gcov_iterator *iter = data; 108 109 if (gcov_iter_next(iter)) 110 return NULL; 111 (*pos)++; 112 113 return iter; 114 } 115 116 /* seq_file.show() implementation for gcov data files. */ 117 static int gcov_seq_show(struct seq_file *seq, void *data) 118 { 119 struct gcov_iterator *iter = data; 120 121 if (gcov_iter_write(iter, seq)) 122 return -EINVAL; 123 return 0; 124 } 125 126 static void gcov_seq_stop(struct seq_file *seq, void *data) 127 { 128 /* Unused. */ 129 } 130 131 static const struct seq_operations gcov_seq_ops = { 132 .start = gcov_seq_start, 133 .next = gcov_seq_next, 134 .show = gcov_seq_show, 135 .stop = gcov_seq_stop, 136 }; 137 138 /* 139 * Return the profiling data set for a given node. This can either be the 140 * original profiling data structure or a duplicate (also called "ghost") 141 * in case the associated object file has been unloaded. 142 */ 143 static struct gcov_info *get_node_info(struct gcov_node *node) 144 { 145 if (node->info) 146 return node->info; 147 148 return node->ghost; 149 } 150 151 /* 152 * open() implementation for gcov data files. Create a copy of the profiling 153 * data set and initialize the iterator and seq_file interface. 154 */ 155 static int gcov_seq_open(struct inode *inode, struct file *file) 156 { 157 struct gcov_node *node = inode->i_private; 158 struct gcov_iterator *iter; 159 struct seq_file *seq; 160 struct gcov_info *info; 161 int rc = -ENOMEM; 162 163 mutex_lock(&node_lock); 164 /* 165 * Read from a profiling data copy to minimize reference tracking 166 * complexity and concurrent access. 167 */ 168 info = gcov_info_dup(get_node_info(node)); 169 if (!info) 170 goto out_unlock; 171 iter = gcov_iter_new(info); 172 if (!iter) 173 goto err_free_info; 174 rc = seq_open(file, &gcov_seq_ops); 175 if (rc) 176 goto err_free_iter_info; 177 seq = file->private_data; 178 seq->private = iter; 179 out_unlock: 180 mutex_unlock(&node_lock); 181 return rc; 182 183 err_free_iter_info: 184 gcov_iter_free(iter); 185 err_free_info: 186 gcov_info_free(info); 187 goto out_unlock; 188 } 189 190 /* 191 * release() implementation for gcov data files. Release resources allocated 192 * by open(). 193 */ 194 static int gcov_seq_release(struct inode *inode, struct file *file) 195 { 196 struct gcov_iterator *iter; 197 struct gcov_info *info; 198 struct seq_file *seq; 199 200 seq = file->private_data; 201 iter = seq->private; 202 info = gcov_iter_get_info(iter); 203 gcov_iter_free(iter); 204 gcov_info_free(info); 205 seq_release(inode, file); 206 207 return 0; 208 } 209 210 /* 211 * Find a node by the associated data file name. Needs to be called with 212 * node_lock held. 213 */ 214 static struct gcov_node *get_node_by_name(const char *name) 215 { 216 struct gcov_node *node; 217 struct gcov_info *info; 218 219 list_for_each_entry(node, &all_head, all) { 220 info = get_node_info(node); 221 if (info && (strcmp(info->filename, name) == 0)) 222 return node; 223 } 224 225 return NULL; 226 } 227 228 static void remove_node(struct gcov_node *node); 229 230 /* 231 * write() implementation for gcov data files. Reset profiling data for the 232 * associated file. If the object file has been unloaded (i.e. this is 233 * a "ghost" node), remove the debug fs node as well. 234 */ 235 static ssize_t gcov_seq_write(struct file *file, const char __user *addr, 236 size_t len, loff_t *pos) 237 { 238 struct seq_file *seq; 239 struct gcov_info *info; 240 struct gcov_node *node; 241 242 seq = file->private_data; 243 info = gcov_iter_get_info(seq->private); 244 mutex_lock(&node_lock); 245 node = get_node_by_name(info->filename); 246 if (node) { 247 /* Reset counts or remove node for unloaded modules. */ 248 if (node->ghost) 249 remove_node(node); 250 else 251 gcov_info_reset(node->info); 252 } 253 /* Reset counts for open file. */ 254 gcov_info_reset(info); 255 mutex_unlock(&node_lock); 256 257 return len; 258 } 259 260 /* 261 * Given a string <path> representing a file path of format: 262 * path/to/file.gcda 263 * construct and return a new string: 264 * <dir/>path/to/file.<ext> 265 */ 266 static char *link_target(const char *dir, const char *path, const char *ext) 267 { 268 char *target; 269 char *old_ext; 270 char *copy; 271 272 copy = kstrdup(path, GFP_KERNEL); 273 if (!copy) 274 return NULL; 275 old_ext = strrchr(copy, '.'); 276 if (old_ext) 277 *old_ext = '\0'; 278 if (dir) 279 target = kasprintf(GFP_KERNEL, "%s/%s.%s", dir, copy, ext); 280 else 281 target = kasprintf(GFP_KERNEL, "%s.%s", copy, ext); 282 kfree(copy); 283 284 return target; 285 } 286 287 /* 288 * Construct a string representing the symbolic link target for the given 289 * gcov data file name and link type. Depending on the link type and the 290 * location of the data file, the link target can either point to a 291 * subdirectory of srctree, objtree or in an external location. 292 */ 293 static char *get_link_target(const char *filename, const struct gcov_link *ext) 294 { 295 const char *rel; 296 char *result; 297 298 if (strncmp(filename, objtree, strlen(objtree)) == 0) { 299 rel = filename + strlen(objtree) + 1; 300 if (ext->dir == SRC_TREE) 301 result = link_target(srctree, rel, ext->ext); 302 else 303 result = link_target(objtree, rel, ext->ext); 304 } else { 305 /* External compilation. */ 306 result = link_target(NULL, filename, ext->ext); 307 } 308 309 return result; 310 } 311 312 #define SKEW_PREFIX ".tmp_" 313 314 /* 315 * For a filename .tmp_filename.ext return filename.ext. Needed to compensate 316 * for filename skewing caused by the mod-versioning mechanism. 317 */ 318 static const char *deskew(const char *basename) 319 { 320 if (strncmp(basename, SKEW_PREFIX, sizeof(SKEW_PREFIX) - 1) == 0) 321 return basename + sizeof(SKEW_PREFIX) - 1; 322 return basename; 323 } 324 325 /* 326 * Create links to additional files (usually .c and .gcno files) which the 327 * gcov tool expects to find in the same directory as the gcov data file. 328 */ 329 static void add_links(struct gcov_node *node, struct dentry *parent) 330 { 331 char *basename; 332 char *target; 333 int num; 334 int i; 335 336 for (num = 0; gcov_link[num].ext; num++) 337 /* Nothing. */; 338 node->links = kcalloc(num, sizeof(struct dentry *), GFP_KERNEL); 339 if (!node->links) 340 return; 341 for (i = 0; i < num; i++) { 342 target = get_link_target(get_node_info(node)->filename, 343 &gcov_link[i]); 344 if (!target) 345 goto out_err; 346 basename = strrchr(target, '/'); 347 if (!basename) 348 goto out_err; 349 basename++; 350 node->links[i] = debugfs_create_symlink(deskew(basename), 351 parent, target); 352 if (!node->links[i]) 353 goto out_err; 354 kfree(target); 355 } 356 357 return; 358 out_err: 359 kfree(target); 360 while (i-- > 0) 361 debugfs_remove(node->links[i]); 362 kfree(node->links); 363 node->links = NULL; 364 } 365 366 static const struct file_operations gcov_data_fops = { 367 .open = gcov_seq_open, 368 .release = gcov_seq_release, 369 .read = seq_read, 370 .llseek = seq_lseek, 371 .write = gcov_seq_write, 372 }; 373 374 /* Basic initialization of a new node. */ 375 static void init_node(struct gcov_node *node, struct gcov_info *info, 376 const char *name, struct gcov_node *parent) 377 { 378 INIT_LIST_HEAD(&node->list); 379 INIT_LIST_HEAD(&node->children); 380 INIT_LIST_HEAD(&node->all); 381 node->info = info; 382 node->parent = parent; 383 if (name) 384 strcpy(node->name, name); 385 } 386 387 /* 388 * Create a new node and associated debugfs entry. Needs to be called with 389 * node_lock held. 390 */ 391 static struct gcov_node *new_node(struct gcov_node *parent, 392 struct gcov_info *info, const char *name) 393 { 394 struct gcov_node *node; 395 396 node = kzalloc(sizeof(struct gcov_node) + strlen(name) + 1, GFP_KERNEL); 397 if (!node) { 398 pr_warning("out of memory\n"); 399 return NULL; 400 } 401 init_node(node, info, name, parent); 402 /* Differentiate between gcov data file nodes and directory nodes. */ 403 if (info) { 404 node->dentry = debugfs_create_file(deskew(node->name), 0600, 405 parent->dentry, node, &gcov_data_fops); 406 } else 407 node->dentry = debugfs_create_dir(node->name, parent->dentry); 408 if (!node->dentry) { 409 pr_warning("could not create file\n"); 410 kfree(node); 411 return NULL; 412 } 413 if (info) 414 add_links(node, parent->dentry); 415 list_add(&node->list, &parent->children); 416 list_add(&node->all, &all_head); 417 418 return node; 419 } 420 421 /* Remove symbolic links associated with node. */ 422 static void remove_links(struct gcov_node *node) 423 { 424 int i; 425 426 if (!node->links) 427 return; 428 for (i = 0; gcov_link[i].ext; i++) 429 debugfs_remove(node->links[i]); 430 kfree(node->links); 431 node->links = NULL; 432 } 433 434 /* 435 * Remove node from all lists and debugfs and release associated resources. 436 * Needs to be called with node_lock held. 437 */ 438 static void release_node(struct gcov_node *node) 439 { 440 list_del(&node->list); 441 list_del(&node->all); 442 debugfs_remove(node->dentry); 443 remove_links(node); 444 if (node->ghost) 445 gcov_info_free(node->ghost); 446 kfree(node); 447 } 448 449 /* Release node and empty parents. Needs to be called with node_lock held. */ 450 static void remove_node(struct gcov_node *node) 451 { 452 struct gcov_node *parent; 453 454 while ((node != &root_node) && list_empty(&node->children)) { 455 parent = node->parent; 456 release_node(node); 457 node = parent; 458 } 459 } 460 461 /* 462 * Find child node with given basename. Needs to be called with node_lock 463 * held. 464 */ 465 static struct gcov_node *get_child_by_name(struct gcov_node *parent, 466 const char *name) 467 { 468 struct gcov_node *node; 469 470 list_for_each_entry(node, &parent->children, list) { 471 if (strcmp(node->name, name) == 0) 472 return node; 473 } 474 475 return NULL; 476 } 477 478 /* 479 * write() implementation for reset file. Reset all profiling data to zero 480 * and remove ghost nodes. 481 */ 482 static ssize_t reset_write(struct file *file, const char __user *addr, 483 size_t len, loff_t *pos) 484 { 485 struct gcov_node *node; 486 487 mutex_lock(&node_lock); 488 restart: 489 list_for_each_entry(node, &all_head, all) { 490 if (node->info) 491 gcov_info_reset(node->info); 492 else if (list_empty(&node->children)) { 493 remove_node(node); 494 /* Several nodes may have gone - restart loop. */ 495 goto restart; 496 } 497 } 498 mutex_unlock(&node_lock); 499 500 return len; 501 } 502 503 /* read() implementation for reset file. Unused. */ 504 static ssize_t reset_read(struct file *file, char __user *addr, size_t len, 505 loff_t *pos) 506 { 507 /* Allow read operation so that a recursive copy won't fail. */ 508 return 0; 509 } 510 511 static const struct file_operations gcov_reset_fops = { 512 .write = reset_write, 513 .read = reset_read, 514 }; 515 516 /* 517 * Create a node for a given profiling data set and add it to all lists and 518 * debugfs. Needs to be called with node_lock held. 519 */ 520 static void add_node(struct gcov_info *info) 521 { 522 char *filename; 523 char *curr; 524 char *next; 525 struct gcov_node *parent; 526 struct gcov_node *node; 527 528 filename = kstrdup(info->filename, GFP_KERNEL); 529 if (!filename) 530 return; 531 parent = &root_node; 532 /* Create directory nodes along the path. */ 533 for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) { 534 if (curr == next) 535 continue; 536 *next = 0; 537 if (strcmp(curr, ".") == 0) 538 continue; 539 if (strcmp(curr, "..") == 0) { 540 if (!parent->parent) 541 goto err_remove; 542 parent = parent->parent; 543 continue; 544 } 545 node = get_child_by_name(parent, curr); 546 if (!node) { 547 node = new_node(parent, NULL, curr); 548 if (!node) 549 goto err_remove; 550 } 551 parent = node; 552 } 553 /* Create file node. */ 554 node = new_node(parent, info, curr); 555 if (!node) 556 goto err_remove; 557 out: 558 kfree(filename); 559 return; 560 561 err_remove: 562 remove_node(parent); 563 goto out; 564 } 565 566 /* 567 * The profiling data set associated with this node is being unloaded. Store a 568 * copy of the profiling data and turn this node into a "ghost". 569 */ 570 static int ghost_node(struct gcov_node *node) 571 { 572 node->ghost = gcov_info_dup(node->info); 573 if (!node->ghost) { 574 pr_warning("could not save data for '%s' (out of memory)\n", 575 node->info->filename); 576 return -ENOMEM; 577 } 578 node->info = NULL; 579 580 return 0; 581 } 582 583 /* 584 * Profiling data for this node has been loaded again. Add profiling data 585 * from previous instantiation and turn this node into a regular node. 586 */ 587 static void revive_node(struct gcov_node *node, struct gcov_info *info) 588 { 589 if (gcov_info_is_compatible(node->ghost, info)) 590 gcov_info_add(info, node->ghost); 591 else { 592 pr_warning("discarding saved data for '%s' (version changed)\n", 593 info->filename); 594 } 595 gcov_info_free(node->ghost); 596 node->ghost = NULL; 597 node->info = info; 598 } 599 600 /* 601 * Callback to create/remove profiling files when code compiled with 602 * -fprofile-arcs is loaded/unloaded. 603 */ 604 void gcov_event(enum gcov_action action, struct gcov_info *info) 605 { 606 struct gcov_node *node; 607 608 mutex_lock(&node_lock); 609 node = get_node_by_name(info->filename); 610 switch (action) { 611 case GCOV_ADD: 612 /* Add new node or revive ghost. */ 613 if (!node) { 614 add_node(info); 615 break; 616 } 617 if (gcov_persist) 618 revive_node(node, info); 619 else { 620 pr_warning("could not add '%s' (already exists)\n", 621 info->filename); 622 } 623 break; 624 case GCOV_REMOVE: 625 /* Remove node or turn into ghost. */ 626 if (!node) { 627 pr_warning("could not remove '%s' (not found)\n", 628 info->filename); 629 break; 630 } 631 if (gcov_persist) { 632 if (!ghost_node(node)) 633 break; 634 } 635 remove_node(node); 636 break; 637 } 638 mutex_unlock(&node_lock); 639 } 640 641 /* Create debugfs entries. */ 642 static __init int gcov_fs_init(void) 643 { 644 int rc = -EIO; 645 646 init_node(&root_node, NULL, NULL, NULL); 647 /* 648 * /sys/kernel/debug/gcov will be parent for the reset control file 649 * and all profiling files. 650 */ 651 root_node.dentry = debugfs_create_dir("gcov", NULL); 652 if (!root_node.dentry) 653 goto err_remove; 654 /* 655 * Create reset file which resets all profiling counts when written 656 * to. 657 */ 658 reset_dentry = debugfs_create_file("reset", 0600, root_node.dentry, 659 NULL, &gcov_reset_fops); 660 if (!reset_dentry) 661 goto err_remove; 662 /* Replay previous events to get our fs hierarchy up-to-date. */ 663 gcov_enable_events(); 664 return 0; 665 666 err_remove: 667 pr_err("init failed\n"); 668 if (root_node.dentry) 669 debugfs_remove(root_node.dentry); 670 671 return rc; 672 } 673 device_initcall(gcov_fs_init); 674