linux/kernel/gcov/fs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  This code exports profiling data as debugfs files to userspace.
   4 *
   5 *    Copyright IBM Corp. 2009
   6 *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
   7 *
   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>
  14 *               Paul Larson
  15 *               Yi CDL Yang
  16 */
  17
  18#define pr_fmt(fmt)     "gcov: " fmt
  19
  20#include <linux/init.h>
  21#include <linux/module.h>
  22#include <linux/debugfs.h>
  23#include <linux/fs.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>
  29#include "gcov.h"
  30
  31/**
  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
  38 *   files.
  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
  45 *
  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.
  50 */
  51struct gcov_node {
  52        struct list_head list;
  53        struct list_head children;
  54        struct list_head all;
  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;
  60        int num_loaded;
  61        char name[];
  62};
  63
  64static const char objtree[] = OBJTREE;
  65static const char srctree[] = SRCTREE;
  66static struct gcov_node root_node;
  67static LIST_HEAD(all_head);
  68static DEFINE_MUTEX(node_lock);
  69
  70/* If non-zero, keep copies of profiling data for unloaded modules. */
  71static int gcov_persist = 1;
  72
  73static int __init gcov_persist_setup(char *str)
  74{
  75        unsigned long val;
  76
  77        if (kstrtoul(str, 0, &val)) {
  78                pr_warn("invalid gcov_persist parameter '%s'\n", str);
  79                return 0;
  80        }
  81        gcov_persist = val;
  82        pr_info("setting gcov_persist to %d\n", gcov_persist);
  83
  84        return 1;
  85}
  86__setup("gcov_persist=", gcov_persist_setup);
  87
  88/*
  89 * seq_file.start() implementation for gcov data files. Note that the
  90 * gcov_iterator interface is designed to be more restrictive than seq_file
  91 * (no start from arbitrary position, etc.), to simplify the iterator
  92 * implementation.
  93 */
  94static void *gcov_seq_start(struct seq_file *seq, loff_t *pos)
  95{
  96        loff_t i;
  97
  98        gcov_iter_start(seq->private);
  99        for (i = 0; i < *pos; i++) {
 100                if (gcov_iter_next(seq->private))
 101                        return NULL;
 102        }
 103        return seq->private;
 104}
 105
 106/* seq_file.next() implementation for gcov data files. */
 107static void *gcov_seq_next(struct seq_file *seq, void *data, loff_t *pos)
 108{
 109        struct gcov_iterator *iter = data;
 110
 111        (*pos)++;
 112        if (gcov_iter_next(iter))
 113                return NULL;
 114
 115        return iter;
 116}
 117
 118/* seq_file.show() implementation for gcov data files. */
 119static int gcov_seq_show(struct seq_file *seq, void *data)
 120{
 121        struct gcov_iterator *iter = data;
 122
 123        if (gcov_iter_write(iter, seq))
 124                return -EINVAL;
 125        return 0;
 126}
 127
 128static void gcov_seq_stop(struct seq_file *seq, void *data)
 129{
 130        /* Unused. */
 131}
 132
 133static const struct seq_operations gcov_seq_ops = {
 134        .start  = gcov_seq_start,
 135        .next   = gcov_seq_next,
 136        .show   = gcov_seq_show,
 137        .stop   = gcov_seq_stop,
 138};
 139
 140/*
 141 * Return a profiling data set associated with the given node. This is
 142 * either a data set for a loaded object file or a data set copy in case
 143 * all associated object files have been unloaded.
 144 */
 145static struct gcov_info *get_node_info(struct gcov_node *node)
 146{
 147        if (node->num_loaded > 0)
 148                return node->loaded_info[0];
 149
 150        return node->unloaded_info;
 151}
 152
 153/*
 154 * Return a newly allocated profiling data set which contains the sum of
 155 * all profiling data associated with the given node.
 156 */
 157static struct gcov_info *get_accumulated_info(struct gcov_node *node)
 158{
 159        struct gcov_info *info;
 160        int i = 0;
 161
 162        if (node->unloaded_info)
 163                info = gcov_info_dup(node->unloaded_info);
 164        else
 165                info = gcov_info_dup(node->loaded_info[i++]);
 166        if (!info)
 167                return NULL;
 168        for (; i < node->num_loaded; i++)
 169                gcov_info_add(info, node->loaded_info[i]);
 170
 171        return info;
 172}
 173
 174/*
 175 * open() implementation for gcov data files. Create a copy of the profiling
 176 * data set and initialize the iterator and seq_file interface.
 177 */
 178static int gcov_seq_open(struct inode *inode, struct file *file)
 179{
 180        struct gcov_node *node = inode->i_private;
 181        struct gcov_iterator *iter;
 182        struct seq_file *seq;
 183        struct gcov_info *info;
 184        int rc = -ENOMEM;
 185
 186        mutex_lock(&node_lock);
 187        /*
 188         * Read from a profiling data copy to minimize reference tracking
 189         * complexity and concurrent access and to keep accumulating multiple
 190         * profiling data sets associated with one node simple.
 191         */
 192        info = get_accumulated_info(node);
 193        if (!info)
 194                goto out_unlock;
 195        iter = gcov_iter_new(info);
 196        if (!iter)
 197                goto err_free_info;
 198        rc = seq_open(file, &gcov_seq_ops);
 199        if (rc)
 200                goto err_free_iter_info;
 201        seq = file->private_data;
 202        seq->private = iter;
 203out_unlock:
 204        mutex_unlock(&node_lock);
 205        return rc;
 206
 207err_free_iter_info:
 208        gcov_iter_free(iter);
 209err_free_info:
 210        gcov_info_free(info);
 211        goto out_unlock;
 212}
 213
 214/*
 215 * release() implementation for gcov data files. Release resources allocated
 216 * by open().
 217 */
 218static int gcov_seq_release(struct inode *inode, struct file *file)
 219{
 220        struct gcov_iterator *iter;
 221        struct gcov_info *info;
 222        struct seq_file *seq;
 223
 224        seq = file->private_data;
 225        iter = seq->private;
 226        info = gcov_iter_get_info(iter);
 227        gcov_iter_free(iter);
 228        gcov_info_free(info);
 229        seq_release(inode, file);
 230
 231        return 0;
 232}
 233
 234/*
 235 * Find a node by the associated data file name. Needs to be called with
 236 * node_lock held.
 237 */
 238static struct gcov_node *get_node_by_name(const char *name)
 239{
 240        struct gcov_node *node;
 241        struct gcov_info *info;
 242
 243        list_for_each_entry(node, &all_head, all) {
 244                info = get_node_info(node);
 245                if (info && (strcmp(gcov_info_filename(info), name) == 0))
 246                        return node;
 247        }
 248
 249        return NULL;
 250}
 251
 252/*
 253 * Reset all profiling data associated with the specified node.
 254 */
 255static void reset_node(struct gcov_node *node)
 256{
 257        int i;
 258
 259        if (node->unloaded_info)
 260                gcov_info_reset(node->unloaded_info);
 261        for (i = 0; i < node->num_loaded; i++)
 262                gcov_info_reset(node->loaded_info[i]);
 263}
 264
 265static void remove_node(struct gcov_node *node);
 266
 267/*
 268 * write() implementation for gcov data files. Reset profiling data for the
 269 * corresponding file. If all associated object files have been unloaded,
 270 * remove the debug fs node as well.
 271 */
 272static ssize_t gcov_seq_write(struct file *file, const char __user *addr,
 273                              size_t len, loff_t *pos)
 274{
 275        struct seq_file *seq;
 276        struct gcov_info *info;
 277        struct gcov_node *node;
 278
 279        seq = file->private_data;
 280        info = gcov_iter_get_info(seq->private);
 281        mutex_lock(&node_lock);
 282        node = get_node_by_name(gcov_info_filename(info));
 283        if (node) {
 284                /* Reset counts or remove node for unloaded modules. */
 285                if (node->num_loaded == 0)
 286                        remove_node(node);
 287                else
 288                        reset_node(node);
 289        }
 290        /* Reset counts for open file. */
 291        gcov_info_reset(info);
 292        mutex_unlock(&node_lock);
 293
 294        return len;
 295}
 296
 297/*
 298 * Given a string <path> representing a file path of format:
 299 *   path/to/file.gcda
 300 * construct and return a new string:
 301 *   <dir/>path/to/file.<ext>
 302 */
 303static char *link_target(const char *dir, const char *path, const char *ext)
 304{
 305        char *target;
 306        char *old_ext;
 307        char *copy;
 308
 309        copy = kstrdup(path, GFP_KERNEL);
 310        if (!copy)
 311                return NULL;
 312        old_ext = strrchr(copy, '.');
 313        if (old_ext)
 314                *old_ext = '\0';
 315        if (dir)
 316                target = kasprintf(GFP_KERNEL, "%s/%s.%s", dir, copy, ext);
 317        else
 318                target = kasprintf(GFP_KERNEL, "%s.%s", copy, ext);
 319        kfree(copy);
 320
 321        return target;
 322}
 323
 324/*
 325 * Construct a string representing the symbolic link target for the given
 326 * gcov data file name and link type. Depending on the link type and the
 327 * location of the data file, the link target can either point to a
 328 * subdirectory of srctree, objtree or in an external location.
 329 */
 330static char *get_link_target(const char *filename, const struct gcov_link *ext)
 331{
 332        const char *rel;
 333        char *result;
 334
 335        if (strncmp(filename, objtree, strlen(objtree)) == 0) {
 336                rel = filename + strlen(objtree) + 1;
 337                if (ext->dir == SRC_TREE)
 338                        result = link_target(srctree, rel, ext->ext);
 339                else
 340                        result = link_target(objtree, rel, ext->ext);
 341        } else {
 342                /* External compilation. */
 343                result = link_target(NULL, filename, ext->ext);
 344        }
 345
 346        return result;
 347}
 348
 349#define SKEW_PREFIX     ".tmp_"
 350
 351/*
 352 * For a filename .tmp_filename.ext return filename.ext. Needed to compensate
 353 * for filename skewing caused by the mod-versioning mechanism.
 354 */
 355static const char *deskew(const char *basename)
 356{
 357        if (strncmp(basename, SKEW_PREFIX, sizeof(SKEW_PREFIX) - 1) == 0)
 358                return basename + sizeof(SKEW_PREFIX) - 1;
 359        return basename;
 360}
 361
 362/*
 363 * Create links to additional files (usually .c and .gcno files) which the
 364 * gcov tool expects to find in the same directory as the gcov data file.
 365 */
 366static void add_links(struct gcov_node *node, struct dentry *parent)
 367{
 368        const char *basename;
 369        char *target;
 370        int num;
 371        int i;
 372
 373        for (num = 0; gcov_link[num].ext; num++)
 374                /* Nothing. */;
 375        node->links = kcalloc(num, sizeof(struct dentry *), GFP_KERNEL);
 376        if (!node->links)
 377                return;
 378        for (i = 0; i < num; i++) {
 379                target = get_link_target(
 380                                gcov_info_filename(get_node_info(node)),
 381                                &gcov_link[i]);
 382                if (!target)
 383                        goto out_err;
 384                basename = kbasename(target);
 385                if (basename == target)
 386                        goto out_err;
 387                node->links[i] = debugfs_create_symlink(deskew(basename),
 388                                                        parent, target);
 389                kfree(target);
 390        }
 391
 392        return;
 393out_err:
 394        kfree(target);
 395        while (i-- > 0)
 396                debugfs_remove(node->links[i]);
 397        kfree(node->links);
 398        node->links = NULL;
 399}
 400
 401static const struct file_operations gcov_data_fops = {
 402        .open           = gcov_seq_open,
 403        .release        = gcov_seq_release,
 404        .read           = seq_read,
 405        .llseek         = seq_lseek,
 406        .write          = gcov_seq_write,
 407};
 408
 409/* Basic initialization of a new node. */
 410static void init_node(struct gcov_node *node, struct gcov_info *info,
 411                      const char *name, struct gcov_node *parent)
 412{
 413        INIT_LIST_HEAD(&node->list);
 414        INIT_LIST_HEAD(&node->children);
 415        INIT_LIST_HEAD(&node->all);
 416        if (node->loaded_info) {
 417                node->loaded_info[0] = info;
 418                node->num_loaded = 1;
 419        }
 420        node->parent = parent;
 421        if (name)
 422                strcpy(node->name, name);
 423}
 424
 425/*
 426 * Create a new node and associated debugfs entry. Needs to be called with
 427 * node_lock held.
 428 */
 429static struct gcov_node *new_node(struct gcov_node *parent,
 430                                  struct gcov_info *info, const char *name)
 431{
 432        struct gcov_node *node;
 433
 434        node = kzalloc(sizeof(struct gcov_node) + strlen(name) + 1, GFP_KERNEL);
 435        if (!node)
 436                goto err_nomem;
 437        if (info) {
 438                node->loaded_info = kcalloc(1, sizeof(struct gcov_info *),
 439                                           GFP_KERNEL);
 440                if (!node->loaded_info)
 441                        goto err_nomem;
 442        }
 443        init_node(node, info, name, parent);
 444        /* Differentiate between gcov data file nodes and directory nodes. */
 445        if (info) {
 446                node->dentry = debugfs_create_file(deskew(node->name), 0600,
 447                                        parent->dentry, node, &gcov_data_fops);
 448        } else
 449                node->dentry = debugfs_create_dir(node->name, parent->dentry);
 450        if (info)
 451                add_links(node, parent->dentry);
 452        list_add(&node->list, &parent->children);
 453        list_add(&node->all, &all_head);
 454
 455        return node;
 456
 457err_nomem:
 458        kfree(node);
 459        pr_warn("out of memory\n");
 460        return NULL;
 461}
 462
 463/* Remove symbolic links associated with node. */
 464static void remove_links(struct gcov_node *node)
 465{
 466        int i;
 467
 468        if (!node->links)
 469                return;
 470        for (i = 0; gcov_link[i].ext; i++)
 471                debugfs_remove(node->links[i]);
 472        kfree(node->links);
 473        node->links = NULL;
 474}
 475
 476/*
 477 * Remove node from all lists and debugfs and release associated resources.
 478 * Needs to be called with node_lock held.
 479 */
 480static void release_node(struct gcov_node *node)
 481{
 482        list_del(&node->list);
 483        list_del(&node->all);
 484        debugfs_remove(node->dentry);
 485        remove_links(node);
 486        kfree(node->loaded_info);
 487        if (node->unloaded_info)
 488                gcov_info_free(node->unloaded_info);
 489        kfree(node);
 490}
 491
 492/* Release node and empty parents. Needs to be called with node_lock held. */
 493static void remove_node(struct gcov_node *node)
 494{
 495        struct gcov_node *parent;
 496
 497        while ((node != &root_node) && list_empty(&node->children)) {
 498                parent = node->parent;
 499                release_node(node);
 500                node = parent;
 501        }
 502}
 503
 504/*
 505 * Find child node with given basename. Needs to be called with node_lock
 506 * held.
 507 */
 508static struct gcov_node *get_child_by_name(struct gcov_node *parent,
 509                                           const char *name)
 510{
 511        struct gcov_node *node;
 512
 513        list_for_each_entry(node, &parent->children, list) {
 514                if (strcmp(node->name, name) == 0)
 515                        return node;
 516        }
 517
 518        return NULL;
 519}
 520
 521/*
 522 * write() implementation for reset file. Reset all profiling data to zero
 523 * and remove nodes for which all associated object files are unloaded.
 524 */
 525static ssize_t reset_write(struct file *file, const char __user *addr,
 526                           size_t len, loff_t *pos)
 527{
 528        struct gcov_node *node;
 529
 530        mutex_lock(&node_lock);
 531restart:
 532        list_for_each_entry(node, &all_head, all) {
 533                if (node->num_loaded > 0)
 534                        reset_node(node);
 535                else if (list_empty(&node->children)) {
 536                        remove_node(node);
 537                        /* Several nodes may have gone - restart loop. */
 538                        goto restart;
 539                }
 540        }
 541        mutex_unlock(&node_lock);
 542
 543        return len;
 544}
 545
 546/* read() implementation for reset file. Unused. */
 547static ssize_t reset_read(struct file *file, char __user *addr, size_t len,
 548                          loff_t *pos)
 549{
 550        /* Allow read operation so that a recursive copy won't fail. */
 551        return 0;
 552}
 553
 554static const struct file_operations gcov_reset_fops = {
 555        .write  = reset_write,
 556        .read   = reset_read,
 557        .llseek = noop_llseek,
 558};
 559
 560/*
 561 * Create a node for a given profiling data set and add it to all lists and
 562 * debugfs. Needs to be called with node_lock held.
 563 */
 564static void add_node(struct gcov_info *info)
 565{
 566        char *filename;
 567        char *curr;
 568        char *next;
 569        struct gcov_node *parent;
 570        struct gcov_node *node;
 571
 572        filename = kstrdup(gcov_info_filename(info), GFP_KERNEL);
 573        if (!filename)
 574                return;
 575        parent = &root_node;
 576        /* Create directory nodes along the path. */
 577        for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) {
 578                if (curr == next)
 579                        continue;
 580                *next = 0;
 581                if (strcmp(curr, ".") == 0)
 582                        continue;
 583                if (strcmp(curr, "..") == 0) {
 584                        if (!parent->parent)
 585                                goto err_remove;
 586                        parent = parent->parent;
 587                        continue;
 588                }
 589                node = get_child_by_name(parent, curr);
 590                if (!node) {
 591                        node = new_node(parent, NULL, curr);
 592                        if (!node)
 593                                goto err_remove;
 594                }
 595                parent = node;
 596        }
 597        /* Create file node. */
 598        node = new_node(parent, info, curr);
 599        if (!node)
 600                goto err_remove;
 601out:
 602        kfree(filename);
 603        return;
 604
 605err_remove:
 606        remove_node(parent);
 607        goto out;
 608}
 609
 610/*
 611 * Associate a profiling data set with an existing node. Needs to be called
 612 * with node_lock held.
 613 */
 614static void add_info(struct gcov_node *node, struct gcov_info *info)
 615{
 616        struct gcov_info **loaded_info;
 617        int num = node->num_loaded;
 618
 619        /*
 620         * Prepare new array. This is done first to simplify cleanup in
 621         * case the new data set is incompatible, the node only contains
 622         * unloaded data sets and there's not enough memory for the array.
 623         */
 624        loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL);
 625        if (!loaded_info) {
 626                pr_warn("could not add '%s' (out of memory)\n",
 627                        gcov_info_filename(info));
 628                return;
 629        }
 630        memcpy(loaded_info, node->loaded_info,
 631               num * sizeof(struct gcov_info *));
 632        loaded_info[num] = info;
 633        /* Check if the new data set is compatible. */
 634        if (num == 0) {
 635                /*
 636                 * A module was unloaded, modified and reloaded. The new
 637                 * data set replaces the copy of the last one.
 638                 */
 639                if (!gcov_info_is_compatible(node->unloaded_info, info)) {
 640                        pr_warn("discarding saved data for %s "
 641                                "(incompatible version)\n",
 642                                gcov_info_filename(info));
 643                        gcov_info_free(node->unloaded_info);
 644                        node->unloaded_info = NULL;
 645                }
 646        } else {
 647                /*
 648                 * Two different versions of the same object file are loaded.
 649                 * The initial one takes precedence.
 650                 */
 651                if (!gcov_info_is_compatible(node->loaded_info[0], info)) {
 652                        pr_warn("could not add '%s' (incompatible "
 653                                "version)\n", gcov_info_filename(info));
 654                        kfree(loaded_info);
 655                        return;
 656                }
 657        }
 658        /* Overwrite previous array. */
 659        kfree(node->loaded_info);
 660        node->loaded_info = loaded_info;
 661        node->num_loaded = num + 1;
 662}
 663
 664/*
 665 * Return the index of a profiling data set associated with a node.
 666 */
 667static int get_info_index(struct gcov_node *node, struct gcov_info *info)
 668{
 669        int i;
 670
 671        for (i = 0; i < node->num_loaded; i++) {
 672                if (node->loaded_info[i] == info)
 673                        return i;
 674        }
 675        return -ENOENT;
 676}
 677
 678/*
 679 * Save the data of a profiling data set which is being unloaded.
 680 */
 681static void save_info(struct gcov_node *node, struct gcov_info *info)
 682{
 683        if (node->unloaded_info)
 684                gcov_info_add(node->unloaded_info, info);
 685        else {
 686                node->unloaded_info = gcov_info_dup(info);
 687                if (!node->unloaded_info) {
 688                        pr_warn("could not save data for '%s' "
 689                                "(out of memory)\n",
 690                                gcov_info_filename(info));
 691                }
 692        }
 693}
 694
 695/*
 696 * Disassociate a profiling data set from a node. Needs to be called with
 697 * node_lock held.
 698 */
 699static void remove_info(struct gcov_node *node, struct gcov_info *info)
 700{
 701        int i;
 702
 703        i = get_info_index(node, info);
 704        if (i < 0) {
 705                pr_warn("could not remove '%s' (not found)\n",
 706                        gcov_info_filename(info));
 707                return;
 708        }
 709        if (gcov_persist)
 710                save_info(node, info);
 711        /* Shrink array. */
 712        node->loaded_info[i] = node->loaded_info[node->num_loaded - 1];
 713        node->num_loaded--;
 714        if (node->num_loaded > 0)
 715                return;
 716        /* Last loaded data set was removed. */
 717        kfree(node->loaded_info);
 718        node->loaded_info = NULL;
 719        node->num_loaded = 0;
 720        if (!node->unloaded_info)
 721                remove_node(node);
 722}
 723
 724/*
 725 * Callback to create/remove profiling files when code compiled with
 726 * -fprofile-arcs is loaded/unloaded.
 727 */
 728void gcov_event(enum gcov_action action, struct gcov_info *info)
 729{
 730        struct gcov_node *node;
 731
 732        mutex_lock(&node_lock);
 733        node = get_node_by_name(gcov_info_filename(info));
 734        switch (action) {
 735        case GCOV_ADD:
 736                if (node)
 737                        add_info(node, info);
 738                else
 739                        add_node(info);
 740                break;
 741        case GCOV_REMOVE:
 742                if (node)
 743                        remove_info(node, info);
 744                else {
 745                        pr_warn("could not remove '%s' (not found)\n",
 746                                gcov_info_filename(info));
 747                }
 748                break;
 749        }
 750        mutex_unlock(&node_lock);
 751}
 752
 753/* Create debugfs entries. */
 754static __init int gcov_fs_init(void)
 755{
 756        init_node(&root_node, NULL, NULL, NULL);
 757        /*
 758         * /sys/kernel/debug/gcov will be parent for the reset control file
 759         * and all profiling files.
 760         */
 761        root_node.dentry = debugfs_create_dir("gcov", NULL);
 762        /*
 763         * Create reset file which resets all profiling counts when written
 764         * to.
 765         */
 766        debugfs_create_file("reset", 0600, root_node.dentry, NULL,
 767                            &gcov_reset_fops);
 768        /* Replay previous events to get our fs hierarchy up-to-date. */
 769        gcov_enable_events();
 770        return 0;
 771}
 772device_initcall(gcov_fs_init);
 773