[
  {
    "path": ".gitattributes",
    "content": ".gitattributes export-ignore\n.gitignore export-ignore\n"
  },
  {
    "path": ".gitignore",
    "content": "*.o\n*.so\n*.a\n*.dat\n*.data\n*.patch\n.*.d\n*.orig\n*.rej\n.pc\n*~\n*.pyc\n*.swp\n\\#*\\#\npatches/\ntc_version.h\nks_version.h\nctracecmd_wrap.c\nctracecmdgui_wrap.c\ntags\nTAGS\ncscope*\ntrace_python_dir\ntracecmd_plugin_dir\nlibtracecmd.pc\nbuild_prefix\nbuild_install\nbuild_libs_install\nltc_version.h\nutest/trace-utest\n"
  },
  {
    "path": "CODING_STYLE",
    "content": "\ntrace-cmd coding-style\n======================\n\nThe coding style of trace-cmd and the tracing libraries (libtracefs and\nlibtraceevent) are very similar to the Linux kernel coding style:\n\n  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding-style.rst\n\nIndentation\n===========\n\nTabs are used for the start of indentation (the '\\t' character), and should be\nset to 8 characters. Spaces may be used at the end for continued lines where\nhaving the start of text line up to braces in the previous line is not\ndivisible by 8.\n\nMax line width\n==============\n\nAll lines should not be more than 100 characters in length.\n\nThis is a guide, as readability is more important than breaking lines up into a\nhard limit. Ideally, strings should never be broken up except for where a new\nline is added.\n\n\tprintf(\"This is a line that may continue for a very long string.\\n\"\n\t       \"This is another line, but after a new line\\n\");\n\nBut line breaks should not be:\n\n\tprintf(\"This is a line that may continue for a very\"\n\t       \"long string.\\n This is another line,\"\n\t       \"but after a new line\\n\");\n\nNot only is the above not as readable as the first version, it is not\neven equivalent, because it is missing spaces between the line breaks.\nFor this reason, finish the string on the same line, even if that string\nbreaks the 100 character limit.\n\nBrackets and braces\n===================\n\nFor all conditionals, the braces start on the same line:\n\n\tif (cond) {\n\t}\n\nAnd the ending brace is at the same indentation as the conditional.\n\n\twhile (cond) {\n\t}\n\n\tdo {\n\t} while (cond);\n\n\tfor (i = 0; i < 10; i++) {\n\t}\n\nThe same is true for structures:\n\n\tstruct my_struct {\n\t\tint field;\n\t};\n\nBut for functions, the braces should start on the following line:\n\n\tvoid my_function(void)\n\t{\n\t}\n\n\nIt is also fine to not use braces for simple conditionals and loops.\n\n\tif (!x)\n\t\ty = x;\n\telse\n\t\ty = 1;\n\n\tfor (i = 0; i < 10; i++)\n\t\tfoo(i);\n\n\twhile (getline(&line, &size, fp) > 0)\n\t\tprintf(\"%s\", line);\n\nBut any complex or multiline conditional or loop should have braces even if it\nis allowed not to by the C language.\n\n\tif (x) {\n\t\tfor (i = 0; i < 10; i++)\n\t\t\tfoo(i);\n\t} else {\n\t\tfoo(1);\n\t}\n\nNotice above that even though the else portion is simple, it too has braces as\nthe else and if blocks should match. If one is required to have braces, they\nboth should have braces.\n\n\nSpaces\n======\n\nA single space should be used between C commands and their starting\nparenthesis.\n\n\tif (x)\n\tfor (i = 0; i < 10; i++)\n\twhile (getline(&line, &size, fp) > 0)\n\nThere should be no space between function or macros and the starting\nparenthesis.\n\n\tfoo(x)\n\tIS_VALID(y)\n\nThis includes prototypes and declarations.\n\n\tvoid foo(int x)\n\nA space should be before and after assignment, comparison and algorithmic\nsigns.\n\n\ti = 0;\n\tif (i < 10)\n\tif (i == 5)\n\n\ty = i + 10;\n\n\ti += 5;\n\nFor structures, use tabs to make all the fields line up nicely.\n\n\tstruct {\n\t\tint\t\t\tfoo;\n\t\tint\t\t\tbar;\n\t\tunsigned long long\ttime;\n\t};\n\nVariable declarations\n=====================\n\nThe order of variables that are declared, should first keep the same types\ntogether, but also should be ordered by their length such that the variables\nare ordered in an \"upside-down Christmas tree\" fashion where the length gets\nsmaller.\n\n\tint tracecmd_count_cpus(void)\n\t{\n\t\tstatic int once;\n\t\tchar buf[1024];\n\t\tint cpus = 0;\n\t\tchar *pbuf;\n\t\tsize_t *pn;\n\t\tFILE *fp;\n\t\tsize_t n;\n\t\tint r;\n\nThe above shows that the order is done by length, and in the above example it\nalso shows that \"int cpu = 0;\" is not grouped next to \"int r;\". As this is more\nof a guideline and made to be more aesthetic to the eye of the reader, both the\nabove and is acceptable as below.\n\n\tint tracecmd_count_cpus(void)\n\t{\n\t\tstatic int once;\n\t\tchar buf[1024];\n\t\tchar *pbuf;\n\t\tsize_t *pn;\n\t\tFILE *fp;\n\t\tsize_t n;\n\t\tint cpus = 0;\n\t\tint r;\n\n\nUnless variables are tightly related, it is expected that each variable be on\nits own line and not grouped by type. That is,\n\n\t\tint r, cpus = 0;\n\nis to be discouraged, as the two variables are not related to each other.\nBut if you had a bunch of counters:\n\n\t\tint i, j, k;\n\nThat would be fine, as the variables are all related as they are all for the\nsame purpose (arbitrary counters). The same may go with pointers;\n\n\n\tchar *begin, *end;\n\nComments\n========\n\nComments will use the \"/* */\" format and the C++ \"//\" style is discouraged.\nIf a comment is on one line, keep the \"/*\" and \"*/\" on the same line:\n\n\t/* This is a single line comment. */\n\nIf a comment spans more than one line, then have the \"/*\" on a separate line\nbefore the comment and the \"*/\" on a separate line at the end of the comment,\nand each line starts with a \"*\" where all the \"*\" line up with each other.\n\n\t/*\n\t * This is a multi line comment, where all the '*'\n\t * will line up, and the text is on a separate line\n\t * as the start and end markers.\n\t */\n\n\nFunction documentation\n======================\n\nAll global functions (and especially any APIs) should have a function\ndescription in the form of \"kernel doc\":\n\n  https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html\n\nThe form is:\n\n  /**\n   * function_name() - Brief description of function.\n   * @arg1: Describe the first argument.\n   * @arg2: Describe the second argument.\n   *        One can provide multiple line descriptions\n   *        for arguments.\n   *\n   * A longer description, with more discussion of the function function_name()\n   * that might be useful to those using or modifying it. Begins with an\n   * empty comment line, and may include additional embedded empty\n   * comment lines.\n   *\n   * The longer description may have multiple paragraphs.\n   *\n   * Context: Describes whether the function can sleep, what locks it takes,\n   *          releases, or expects to be held. It can extend over multiple\n   *          lines.\n   * Return: Describe the return value of function_name.\n   *\n   * The return value description can also have multiple paragraphs, and should\n   * be placed at the end of the comment block.\n   */\n\nStructure layout\n================\n\nThis is more about compaction than coding style. When creating structures, be\naware that if the fields are placed together without being sized by alignment,\nthat the compiler will create \"holes\" in them.\n\n\tstruct {\n\t\tint\t\t\tx;\n\t\tchar\t\t\ty;\n\t\tunsigned long long\tf;\n\t};\n\nAs int is 4 bytes in length, char is one byte, and unsigned long long is 8\nbytes. The compiler will try to naturally align them by their size, and will\ninclude padding (holes) inside the structure to do so. The above is equivalent\nto:\n\n\tstruct {\n\t\tint\t\t\tx;\n\t\tchar\t\t\ty;\n\t\tchar\t\t\tpadding[3];\n\t\tunsigned long long\tf;\n\t};\n\nIt is best to try to organize the structure where there are no holes within\nthem.\n\n\tstruct {\n\t\tunsigned long long\tf;\n\t\tint\t\t\tx;\n\t\tchar\t\t\ty;\n\t};\n\nThe above is better formatting, even if there may be padding outside the\nstructure, but the compiler will still have more flexibility to utilize the\nspace outside the structure than what it can do within it.\n\nGeneral\n=======\n\nAs stated, this is a guide and may not be strictly enforced. The goal is to\nhave consistent and readable code. In general, try to have the coding style\nmatch the surrounding code.\n"
  },
  {
    "path": "CONTRIBUTE",
    "content": "If you like to become part of the community and submit patches, here's how\nto do so for trace-cmd.\n\nIf you only want to report a bug, or suggest an enhancement, you may do\nso at:\n\n  https://bugzilla.kernel.org/buglist.cgi?component=Trace-cmd%2FKernelshark\n\nAll development is done via a mailing list:\n\n   http://vger.kernel.org/vger-lists.html#linux-trace-devel\n\nPatches should be sent to linux-trace-devel@vger.kernel.org\n\nStart by cloning the official repository:\n\n  git clone git://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git\n\nMake your changes. When you are satisfied with them, commit them into git.\nHere's some helpful hints for your git commits.\n\n1) When making changes, please follow the coding style defined by the file\n   called CODING_STYLE in this directory.\n\n2) Every commit should only do one thing.\n   That is, if your work requires some cleaning up of code, do that\n   clean up as a separate commit and not with your functional changes.\n   Find ways to take \"steps\" in modifying code. If you can break up\n   your changes in a series of steps, do so.\n\n3) The commit log should start with a title. Like the below\n\n      trace-cmd: Add CONTRIBUTE file\n\n   Even though this repo is for trace-cmd, start the topic with\n   \"trace-cmd:\" because the commits will end up as patches to a mailing\n   list that handles other tracing repos, differentiating them with the subject\n   is useful. You can be more specific as well. If the change only affects the\n   \"record\" command, you may start the title with \"trace-cmd record:\".\n\n4) The body of the commit (with a blank line from the title), should be self\n   contained, and explain why you are making the change. The title should hold\n   the \"what\" is changing, but the body contains the rationale for the change.\n   It should be a stand alone, and not state things like \"See the next patch\",\n   because when it is in git history, there's no knowing what the next patch\n   is.  You can make statements like \"This is needed for a <future-feature>\n   that will come later\". Where \"<future-feature>\" is something that you are\n   working on and the current commit is one of the steps required to get there.\n\n5) Add your Developer Certificate of Origin (DCO) at the bottom of the commit\n   log. That is \"Signed-off-by: Full Name <email>\" where your full name is your\n   real name (no pseudonyms). Optionally, if you are making the change on\n   behalf of your company, you may also add your company name, if you are not\n   using your company's email. \"Signed-off-by: Full Name (Company) <email>\".\n   Please note, the DCO is your statement that you have the legal right to\n   make these changes for the project you are submitting to.\n\nYou can use the Linux kernel \"checkpatch.pl\" script to help verify the formatting\nof your patch:\n\n  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/scripts/checkpatch.pl\n\nPlease note that checkpatch.pl is a guide and not a hard rule. If it reports a\nfix that makes the code harder to read, that fix can probably be ignored.\n\n  git format-patch --stdout HEAD~1..HEAD | ./checkpatch.pl\n\nFinally, you can use the git \"send-email\" functionality:\n\n  git send-email --from='<your-email> --to='linux-trace-devel@vger.kernel.org' HEAD~1..HEAD\n\nIf you are sending one patch, if you are adding more than one patch, also include\na cover letter:\n\n  git send-email --cover-letter --annotate --from='<your-email> --to='linux-trace-devel@vger.kernel.org' <first-commit>~1..HEAD\n\nIf you receive feedback on your patches, and plan on sending another version,\nplease use the '-v' option to mark your patches that they are a new version.\nFor example, if you add \"-v2\" to the above commands, instead of having:\n\"[PATCH]\" in the subject, it will have \"[PATCH v2]\", letting the reviewers know\nthat this is a new version. If you send another version, use \"-v3\" and so on.\n\nFor more information about git send-email:\n\n  https://git-scm.com/docs/git-send-email\n\nTo keep track of the status of patches that have been submitted, check out:\n\n  https://patchwork.kernel.org/project/linux-trace-devel/list/\n\nIf you would like to apply patches from the mailing list, you can use\nthe \"b4\" utility.\n\n $ pip install b4\n\nThen from the mailing list archive, find a message id from a patch or patch\nseries. For example, to get the patch from:\n\n  https://lore.kernel.org/linux-trace-devel/20210205173713.132051-1-tz.stoyanov@gmail.com/\n\n $ b4 am -o - 20210205173713.132051-1-tz.stoyanov@gmail.com > /tmp/p.mbox\n $ git am /tmp/p.mbox\n\n"
  },
  {
    "path": "COPYING",
    "content": "There are two main licenses that the tools in this directory are covered\nunder. For the applications themselves, they are covered under GPL-2.0 (see\nLICENSES/GPL-2.0). As for the exported headers and libraries, they are covered\nunder LPGL-2.1 (see LICENSES/LGPL-2.1).\n"
  },
  {
    "path": "COPYING.LIB",
    "content": "\n                  GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 2.1, February 1999\n\n Copyright (C) 1991, 1999 Free Software Foundation, Inc.\n 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n[This is the first released version of the Lesser GPL.  It also counts\n as the successor of the GNU Library Public License, version 2, hence\n the version number 2.1.]\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicenses are intended to guarantee your freedom to share and change\nfree software--to make sure the software is free for all its users.\n\n  This license, the Lesser General Public License, applies to some\nspecially designated software packages--typically libraries--of the\nFree Software Foundation and other authors who decide to use it.  You\ncan use it too, but we suggest you first think carefully about whether\nthis license or the ordinary General Public License is the better\nstrategy to use in any particular case, based on the explanations\nbelow.\n\n  When we speak of free software, we are referring to freedom of use,\nnot price.  Our General Public Licenses are designed to make sure that\nyou have the freedom to distribute copies of free software (and charge\nfor this service if you wish); that you receive source code or can get\nit if you want it; that you can change the software and use pieces of\nit in new free programs; and that you are informed that you can do\nthese things.\n\n  To protect your rights, we need to make restrictions that forbid\ndistributors to deny you these rights or to ask you to surrender these\nrights.  These restrictions translate to certain responsibilities for\nyou if you distribute copies of the library or if you modify it.\n\n  For example, if you distribute copies of the library, whether gratis\nor for a fee, you must give the recipients all the rights that we gave\nyou.  You must make sure that they, too, receive or can get the source\ncode.  If you link other code with the library, you must provide\ncomplete object files to the recipients, so that they can relink them\nwith the library after making changes to the library and recompiling\nit.  And you must show them these terms so they know their rights.\n\n  We protect your rights with a two-step method: (1) we copyright the\nlibrary, and (2) we offer you this license, which gives you legal\npermission to copy, distribute and/or modify the library.\n\n  To protect each distributor, we want to make it very clear that\nthere is no warranty for the free library.  Also, if the library is\nmodified by someone else and passed on, the recipients should know\nthat what they have is not the original version, so that the original\nauthor's reputation will not be affected by problems that might be\nintroduced by others.\n^L\n  Finally, software patents pose a constant threat to the existence of\nany free program.  We wish to make sure that a company cannot\neffectively restrict the users of a free program by obtaining a\nrestrictive license from a patent holder.  Therefore, we insist that\nany patent license obtained for a version of the library must be\nconsistent with the full freedom of use specified in this license.\n\n  Most GNU software, including some libraries, is covered by the\nordinary GNU General Public License.  This license, the GNU Lesser\nGeneral Public License, applies to certain designated libraries, and\nis quite different from the ordinary General Public License.  We use\nthis license for certain libraries in order to permit linking those\nlibraries into non-free programs.\n\n  When a program is linked with a library, whether statically or using\na shared library, the combination of the two is legally speaking a\ncombined work, a derivative of the original library.  The ordinary\nGeneral Public License therefore permits such linking only if the\nentire combination fits its criteria of freedom.  The Lesser General\nPublic License permits more lax criteria for linking other code with\nthe library.\n\n  We call this license the \"Lesser\" General Public License because it\ndoes Less to protect the user's freedom than the ordinary General\nPublic License.  It also provides other free software developers Less\nof an advantage over competing non-free programs.  These disadvantages\nare the reason we use the ordinary General Public License for many\nlibraries.  However, the Lesser license provides advantages in certain\nspecial circumstances.\n\n  For example, on rare occasions, there may be a special need to\nencourage the widest possible use of a certain library, so that it\nbecomes a de-facto standard.  To achieve this, non-free programs must\nbe allowed to use the library.  A more frequent case is that a free\nlibrary does the same job as widely used non-free libraries.  In this\ncase, there is little to gain by limiting the free library to free\nsoftware only, so we use the Lesser General Public License.\n\n  In other cases, permission to use a particular library in non-free\nprograms enables a greater number of people to use a large body of\nfree software.  For example, permission to use the GNU C Library in\nnon-free programs enables many more people to use the whole GNU\noperating system, as well as its variant, the GNU/Linux operating\nsystem.\n\n  Although the Lesser General Public License is Less protective of the\nusers' freedom, it does ensure that the user of a program that is\nlinked with the Library has the freedom and the wherewithal to run\nthat program using a modified version of the Library.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.  Pay close attention to the difference between a\n\"work based on the library\" and a \"work that uses the library\".  The\nformer contains code derived from the library, whereas the latter must\nbe combined with the library in order to run.\n^L\n                  GNU LESSER GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License Agreement applies to any software library or other\nprogram which contains a notice placed by the copyright holder or\nother authorized party saying it may be distributed under the terms of\nthis Lesser General Public License (also called \"this License\").\nEach licensee is addressed as \"you\".\n\n  A \"library\" means a collection of software functions and/or data\nprepared so as to be conveniently linked with application programs\n(which use some of those functions and data) to form executables.\n\n  The \"Library\", below, refers to any such software library or work\nwhich has been distributed under these terms.  A \"work based on the\nLibrary\" means either the Library or any derivative work under\ncopyright law: that is to say, a work containing the Library or a\nportion of it, either verbatim or with modifications and/or translated\nstraightforwardly into another language.  (Hereinafter, translation is\nincluded without limitation in the term \"modification\".)\n\n  \"Source code\" for a work means the preferred form of the work for\nmaking modifications to it.  For a library, complete source code means\nall the source code for all modules it contains, plus any associated\ninterface definition files, plus the scripts used to control\ncompilation and installation of the library.\n\n  Activities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning a program using the Library is not restricted, and output from\nsuch a program is covered only if its contents constitute a work based\non the Library (independent of the use of the Library in a tool for\nwriting it).  Whether that is true depends on what the Library does\nand what the program that uses the Library does.\n\n  1. You may copy and distribute verbatim copies of the Library's\ncomplete source code as you receive it, in any medium, provided that\nyou conspicuously and appropriately publish on each copy an\nappropriate copyright notice and disclaimer of warranty; keep intact\nall the notices that refer to this License and to the absence of any\nwarranty; and distribute a copy of this License along with the\nLibrary.\n\n  You may charge a fee for the physical act of transferring a copy,\nand you may at your option offer warranty protection in exchange for a\nfee.\n\f\n  2. You may modify your copy or copies of the Library or any portion\nof it, thus forming a work based on the Library, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) The modified work must itself be a software library.\n\n    b) You must cause the files modified to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    c) You must cause the whole of the work to be licensed at no\n    charge to all third parties under the terms of this License.\n\n    d) If a facility in the modified Library refers to a function or a\n    table of data to be supplied by an application program that uses\n    the facility, other than as an argument passed when the facility\n    is invoked, then you must make a good faith effort to ensure that,\n    in the event an application does not supply such function or\n    table, the facility still operates, and performs whatever part of\n    its purpose remains meaningful.\n\n    (For example, a function in a library to compute square roots has\n    a purpose that is entirely well-defined independent of the\n    application.  Therefore, Subsection 2d requires that any\n    application-supplied function or table used by this function must\n    be optional: if the application does not supply it, the square\n    root function must still compute square roots.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Library,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Library, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote\nit.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Library.\n\nIn addition, mere aggregation of another work not based on the Library\nwith the Library (or with a work based on the Library) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may opt to apply the terms of the ordinary GNU General Public\nLicense instead of this License to a given copy of the Library.  To do\nthis, you must alter all the notices that refer to this License, so\nthat they refer to the ordinary GNU General Public License, version 2,\ninstead of to this License.  (If a newer version than version 2 of the\nordinary GNU General Public License has appeared, then you can specify\nthat version instead if you wish.)  Do not make any other change in\nthese notices.\n^L\n  Once this change is made in a given copy, it is irreversible for\nthat copy, so the ordinary GNU General Public License applies to all\nsubsequent copies and derivative works made from that copy.\n\n  This option is useful when you wish to copy part of the code of\nthe Library into a program that is not a library.\n\n  4. You may copy and distribute the Library (or a portion or\nderivative of it, under Section 2) in object code or executable form\nunder the terms of Sections 1 and 2 above provided that you accompany\nit with the complete corresponding machine-readable source code, which\nmust be distributed under the terms of Sections 1 and 2 above on a\nmedium customarily used for software interchange.\n\n  If distribution of object code is made by offering access to copy\nfrom a designated place, then offering equivalent access to copy the\nsource code from the same place satisfies the requirement to\ndistribute the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  5. A program that contains no derivative of any portion of the\nLibrary, but is designed to work with the Library by being compiled or\nlinked with it, is called a \"work that uses the Library\".  Such a\nwork, in isolation, is not a derivative work of the Library, and\ntherefore falls outside the scope of this License.\n\n  However, linking a \"work that uses the Library\" with the Library\ncreates an executable that is a derivative of the Library (because it\ncontains portions of the Library), rather than a \"work that uses the\nlibrary\".  The executable is therefore covered by this License.\nSection 6 states terms for distribution of such executables.\n\n  When a \"work that uses the Library\" uses material from a header file\nthat is part of the Library, the object code for the work may be a\nderivative work of the Library even though the source code is not.\nWhether this is true is especially significant if the work can be\nlinked without the Library, or if the work is itself a library.  The\nthreshold for this to be true is not precisely defined by law.\n\n  If such an object file uses only numerical parameters, data\nstructure layouts and accessors, and small macros and small inline\nfunctions (ten lines or less in length), then the use of the object\nfile is unrestricted, regardless of whether it is legally a derivative\nwork.  (Executables containing this object code plus portions of the\nLibrary will still fall under Section 6.)\n\n  Otherwise, if the work is a derivative of the Library, you may\ndistribute the object code for the work under the terms of Section 6.\nAny executables containing that work also fall under Section 6,\nwhether or not they are linked directly with the Library itself.\n^L\n  6. As an exception to the Sections above, you may also combine or\nlink a \"work that uses the Library\" with the Library to produce a\nwork containing portions of the Library, and distribute that work\nunder terms of your choice, provided that the terms permit\nmodification of the work for the customer's own use and reverse\nengineering for debugging such modifications.\n\n  You must give prominent notice with each copy of the work that the\nLibrary is used in it and that the Library and its use are covered by\nthis License.  You must supply a copy of this License.  If the work\nduring execution displays copyright notices, you must include the\ncopyright notice for the Library among them, as well as a reference\ndirecting the user to the copy of this License.  Also, you must do one\nof these things:\n\n    a) Accompany the work with the complete corresponding\n    machine-readable source code for the Library including whatever\n    changes were used in the work (which must be distributed under\n    Sections 1 and 2 above); and, if the work is an executable linked\n    with the Library, with the complete machine-readable \"work that\n    uses the Library\", as object code and/or source code, so that the\n    user can modify the Library and then relink to produce a modified\n    executable containing the modified Library.  (It is understood\n    that the user who changes the contents of definitions files in the\n    Library will not necessarily be able to recompile the application\n    to use the modified definitions.)\n\n    b) Use a suitable shared library mechanism for linking with the\n    Library.  A suitable mechanism is one that (1) uses at run time a\n    copy of the library already present on the user's computer system,\n    rather than copying library functions into the executable, and (2)\n    will operate properly with a modified version of the library, if\n    the user installs one, as long as the modified version is\n    interface-compatible with the version that the work was made with.\n\n    c) Accompany the work with a written offer, valid for at least\n    three years, to give the same user the materials specified in\n    Subsection 6a, above, for a charge no more than the cost of\n    performing this distribution.\n\n    d) If distribution of the work is made by offering access to copy\n    from a designated place, offer equivalent access to copy the above\n    specified materials from the same place.\n\n    e) Verify that the user has already received a copy of these\n    materials or that you have already sent this user a copy.\n\n  For an executable, the required form of the \"work that uses the\nLibrary\" must include any data and utility programs needed for\nreproducing the executable from it.  However, as a special exception,\nthe materials to be distributed need not include anything that is\nnormally distributed (in either source or binary form) with the major\ncomponents (compiler, kernel, and so on) of the operating system on\nwhich the executable runs, unless that component itself accompanies\nthe executable.\n\n  It may happen that this requirement contradicts the license\nrestrictions of other proprietary libraries that do not normally\naccompany the operating system.  Such a contradiction means you cannot\nuse both them and the Library together in an executable that you\ndistribute.\n^L\n  7. You may place library facilities that are a work based on the\nLibrary side-by-side in a single library together with other library\nfacilities not covered by this License, and distribute such a combined\nlibrary, provided that the separate distribution of the work based on\nthe Library and of the other library facilities is otherwise\npermitted, and provided that you do these two things:\n\n    a) Accompany the combined library with a copy of the same work\n    based on the Library, uncombined with any other library\n    facilities.  This must be distributed under the terms of the\n    Sections above.\n\n    b) Give prominent notice with the combined library of the fact\n    that part of it is a work based on the Library, and explaining\n    where to find the accompanying uncombined form of the same work.\n\n  8. You may not copy, modify, sublicense, link with, or distribute\nthe Library except as expressly provided under this License.  Any\nattempt otherwise to copy, modify, sublicense, link with, or\ndistribute the Library is void, and will automatically terminate your\nrights under this License.  However, parties who have received copies,\nor rights, from you under this License will not have their licenses\nterminated so long as such parties remain in full compliance.\n\n  9. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Library or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Library (or any work based on the\nLibrary), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Library or works based on it.\n\n  10. Each time you redistribute the Library (or any work based on the\nLibrary), the recipient automatically receives a license from the\noriginal licensor to copy, distribute, link with or modify the Library\nsubject to these terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties with\nthis License.\n^L\n  11. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Library at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Library by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Library.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply, and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  12. If the distribution and/or use of the Library is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Library under this License\nmay add an explicit geographical distribution limitation excluding those\ncountries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  13. The Free Software Foundation may publish revised and/or new\nversions of the Lesser General Public License from time to time.\nSuch new versions will be similar in spirit to the present version,\nbut may differ in detail to address new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Library\nspecifies a version number of this License which applies to it and\n\"any later version\", you have the option of following the terms and\nconditions either of that version or of any later version published by\nthe Free Software Foundation.  If the Library does not specify a\nlicense version number, you may choose any version ever published by\nthe Free Software Foundation.\n^L\n  14. If you wish to incorporate parts of the Library into other free\nprograms whose distribution conditions are incompatible with these,\nwrite to the author to ask for permission.  For software which is\ncopyrighted by the Free Software Foundation, write to the Free\nSoftware Foundation; we sometimes make exceptions for this.  Our\ndecision will be guided by the two goals of preserving the free status\nof all derivatives of our free software and of promoting the sharing\nand reuse of software generally.\n\n                            NO WARRANTY\n\n  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO\nWARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.\nEXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR\nOTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY\nKIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE\nLIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME\nTHE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\nWRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY\nAND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU\nFOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR\nCONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE\nLIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING\nRENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A\nFAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF\nSUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n^L\n           How to Apply These Terms to Your New Libraries\n\n  If you develop a new library, and you want it to be of the greatest\npossible use to the public, we recommend making it free software that\neveryone can redistribute and change.  You can do so by permitting\nredistribution under these terms (or, alternatively, under the terms\nof the ordinary General Public License).\n\n  To apply these terms, attach the following notices to the library.\nIt is safest to attach them to the start of each source file to most\neffectively convey the exclusion of warranty; and each file should\nhave at least the \"copyright\" line and a pointer to where the full\nnotice is found.\n\n\n    <one line to give the library's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This library is free software; you can redistribute it and/or\n    modify it under the terms of the GNU Lesser General Public\n    License as published by the Free Software Foundation; either\n    version 2.1 of the License, or (at your option) any later version.\n\n    This library is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n    Lesser General Public License for more details.\n\n    You should have received a copy of the GNU Lesser General Public\n    License along with this library; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA\n\nAlso add information on how to contact you by electronic and paper mail.\n\nYou should also get your employer (if you work as a programmer) or\nyour school, if any, to sign a \"copyright disclaimer\" for the library,\nif necessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the\n  library `Frob' (a library for tweaking knobs) written by James\n  Random Hacker.\n\n  <signature of Ty Coon>, 1 April 1990\n  Ty Coon, President of Vice\n\nThat's all there is to it!\n\n\n"
  },
  {
    "path": "DCO",
    "content": "\n(Copied from the Linux Kernel's Documentation/process/submitting-patches.rst)\n\nSign your work - the Developer's Certificate of Origin\n------------------------------------------------------\n\nThe sign-off is a simple line at the end of the explanation for the\npatch, which certifies that you wrote it or otherwise have the right to\npass it on as an open-source patch.  The rules are pretty simple: if you\ncan certify the below:\n\nDeveloper's Certificate of Origin 1.1\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nBy making a contribution to this project, I certify that:\n\n        (a) The contribution was created in whole or in part by me and I\n            have the right to submit it under the open source license\n            indicated in the file; or\n\n        (b) The contribution is based upon previous work that, to the best\n            of my knowledge, is covered under an appropriate open source\n            license and I have the right under that license to submit that\n            work with modifications, whether created in whole or in part\n            by me, under the same open source license (unless I am\n            permitted to submit under a different license), as indicated\n            in the file; or\n\n        (c) The contribution was provided directly to me by some other\n            person who certified (a), (b) or (c) and I have not modified\n            it.\n\n        (d) I understand and agree that this project and the contribution\n            are public and that a record of the contribution (including all\n            personal information I submit with it, including my sign-off) is\n            maintained indefinitely and may be redistributed consistent with\n            this project or the open source license(s) involved.\n\nthen you just add a line saying::\n\n        Signed-off-by: Random J Developer <random@developer.example.org>\n\nusing your real name (sorry, no pseudonyms or anonymous contributions.)\n\nSome people also put extra tags at the end.  They'll just be ignored for\nnow, but you can do this to mark internal company procedures or just\npoint out some special detail about the sign-off.\n"
  },
  {
    "path": "Documentation/.gitignore",
    "content": "*.[1-9]\n*.m\n*.html\n"
  },
  {
    "path": "Documentation/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\ndoc_dir:=$(src)/Documentation\n\nexport doc_dir\n\nSUBDIR += trace-cmd\nSUBDIR += libtracecmd\n\n.PHONY: $(SUBDIR)\n\nDOCDIR = $(src)/Documentation\nASCIIDOC=asciidoc\nASCIIDOC_CONF\t =  $(DOCDIR)/asciidoc.conf\nASCIIDOC_EXTRA = --unsafe -f $(ASCIIDOC_CONF)\nASCIIDOC_HTML = xhtml11\nMANPAGE_XSL = $(DOCDIR)/manpage-normal.xsl\nXMLTO_EXTRA =\nINSTALL?=install\nRM ?= rm -f\n\nASCIIDOC_INSTALLED := $(shell command -v $(ASCIIDOC) 2> /dev/null)\nifndef ASCIIDOC_INSTALLED\n\tmissing_tools += $(ASCIIDOC)\nendif\n\nXMLTO=xmlto\nXMLTO_INSTALLED := $(shell command -v $(XMLTO) 2> /dev/null)\nifndef XMLTO_INSTALLED\n\tmissing_tools += $(XMLTO)\nendif\n\n#\n# For asciidoc ...\n#\t-7.1.2,\tno extra settings are needed.\n#\t8.0-,\tset ASCIIDOC8.\n#\n\n#\n# For docbook-xsl ...\n#\t-1.68.1,\tset ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)\n#\t1.69.0,\t\tno extra settings are needed?\n#\t1.69.1-1.71.0,\tset DOCBOOK_SUPPRESS_SP?\n#\t1.71.1,\t\tno extra settings are needed?\n#\t1.72.0,\t\tset DOCBOOK_XSL_172.\n#\t1.73.0-,\tset ASCIIDOC_NO_ROFF\n#\n\n#\n# If you had been using DOCBOOK_XSL_172 in an attempt to get rid\n# of 'the \".ft C\" problem' in your generated manpages, and you\n# instead ended up with weird characters around callouts, try\n# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).\n#\n\nifdef ASCIIDOC8\nASCIIDOC_EXTRA += -a asciidoc7compatible\nendif\nifdef DOCBOOK_XSL_172\nASCIIDOC_EXTRA += -a libtracecmd-asciidoc-no-roff\nMANPAGE_XSL = $(DOCDIR)/manpage-1.72.xsl\nelse\n\tifdef ASCIIDOC_NO_ROFF\n\t# docbook-xsl after 1.72 needs the regular XSL, but will not\n\t# pass-thru raw roff codes from asciidoc.conf, so turn them off.\n\tASCIIDOC_EXTRA += -a libtracecmd-asciidoc-no-roff\n\tendif\nendif\nifdef MAN_BOLD_LITERAL\nXMLTO_EXTRA += -m $(DOCDIR)/manpage-bold-literal.xsl\nendif\nifdef DOCBOOK_SUPPRESS_SP\nXMLTO_EXTRA += -m $(DOCDIR)/manpage-suppress-sp.xsl\nendif\n\nifdef USE_ASCIIDOCTOR\nASCIIDOC = asciidoctor\nASCIIDOC_EXTRA = -a compat-mode\nASCIIDOC_EXTRA += -I. -rasciidoctor-extensions\nASCIIDOC_HTML = xhtml5\nendif\n\nifneq ($(findstring $(MAKEFLAGS),w),w)\nPRINT_DIR = --no-print-directory\nelse # \"make -w\"\nNO_SUBDIR = :\nendif\n\nexport ASCIIDOC ASCIIDOC_CONF ASCIIDOC_EXTRA ASCIIDOC_HTML\nexport MANPAGE_XSL\nexport XMLTO XMLTO_INSTALLED XMLTO_EXTRA\nexport  missing_tools\nexport RM\n\nall: $(SUBDIR)\nclean: $(SUBDIR)\ninstall: $(SUBDIR)\n\n$(SUBDIR):\n\tmake -C $@ $(MAKECMDGOALS)\n\n"
  },
  {
    "path": "Documentation/README.PythonPlugin",
    "content": " PYTHON PLUGIN DOCUMENTATION\n=============================\n\nWith the python plugin (make python-plugin) you can now\nwrite plugins in python. The API exported by the python\nplugin itself (written in C) allows you to access most\ninformation about a record from python.\n\nTo write a python plugin, put a new .py file into a new\n~/.trace-cmd/python/ directory.\n\nThe most basic python plugin is this:\n\n--- %< ---\ndef register(pevent):\n    pass\n--- >% ---\n\nwhich obviously does nothing at all.\n\nTo register a callback, use the pevent.register_event_handler\nfunction:\n\n--- %< ---\nimport tracecmd\n\ndef my_event_handler(trace_seq, event):\n    pass\n\ndef register(pevent):\n    pevent.register_event_handler(\"subsys\", \"event_name\",\n                                  my_event_handler)\n--- >% ---\n\n\nThere are four object types that you get, described below.\n\n tracecmd.PEvent\n-----------------\n\nThis is the class of the 'pevent' object above,\nyou get one of those via your register callback.\nIt has one method and one property:\n * register_event_handler() - example above, to register\n                              an event handler function\n * file_endian              - either '<' or '>' indicating\n                              which endianness the file has,\n                              to be used with struct.unpack()\n\n tracecmd.TraceSeq\n-------------------\n\nThis is the class of the 'trace_seq' parameter to your callback\nfunction. It has only one method, puts(), to put data into the\nbuffer. Formatting must be done in python.\n\n tracecmd.Event\n----------------------\n\nThis is the class of the 'event' parameter to your callback\nfunction. Note that it doesn't just contain the format, but\nalso the event data. As such, you can do much with this, and\nthis is what you'll usually use. Each instance of this allows\naccess to record items via the dict protocol, and you can get\nthe items via its keys() methods. So for example, your\ncallback could be\n\n--- %< ---\ndef my_callback(trace_seq, event):\n    for fieldname in event.keys():\n        field = event[fieldname]\n--- >% ---\n\nEach field returned from the dict protocol is an instance of\nthe next (and last) class:\n\n tracecmd.Field\n----------------------\n\nThis is an instance of a field, including its data. It affords\nnumerous use cases and is what you'll be using most.\n\n * If this is an integer field, i.e. 1, 2, 4 or 8 bytes long,\n   you can convert it to the number contained, according to\n   the file's endianness, by simply casting it to a long:\n\n     field = event['myint']\n     value = long(field)\n\n * You can access the field's data, as field.data, and if the\n   data is really a \"__data_loc\" type that will be resolved\n   automatically. (If you don't know what this means, don't\n   worry about it and just use field.data)\n\n\nThis is it. It's pretty simple. A fully-featured plugin could\nlook like this:\n\n--- %< ---\ndef my_event_handler(trace_seq, event):\n    trace_seq.puts(\"myev: %u\", long(event['myfield']))\n\ndef register(pevent):\n    pevent.register_event_handler(\"subsys\", \"event_name\",\n                                  my_event_handler)\n--- >% ---\n\n\n Tips and tricks\n-----------------\n\nBe familiar with the struct module and use it, always\nchecking endianness and potentially using pevent.file_endian.\n\n\nIf you need access to pevent in your callbacks, simply\npass it in yourself:\n\n--- %< ---\ndef my_event_handler(pevent, trace_seq, event):\n    pass\n\ndef register(pevent):\n    pevent.register_event_handler(\"subsys\", \"event_name\",\n        lambda *args: my_event_handler(pevent, *args)\n    )\n--- >% ---\n"
  },
  {
    "path": "Documentation/asciidoc.conf",
    "content": "## linktep: macro\n#\n# Usage: linktep:command[manpage-section]\n#\n# Note, {0} is the manpage section, while {target} is the command.\n#\n# Show TEP link as: <command>(<section>); if section is defined, else just show\n# the command.\n\n[macros]\n(?su)[\\\\]?(?P<name>linktep):(?P<target>\\S*?)\\[(?P<attrlist>.*?)\\]=\n\n[attributes]\nasterisk=&#42;\nplus=&#43;\ncaret=&#94;\nstartsb=&#91;\nendsb=&#93;\ntilde=&#126;\n\nifdef::backend-docbook[]\n[linktep-inlinemacro]\n{0%{target}}\n{0#<citerefentry>}\n{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}\n{0#</citerefentry>}\nendif::backend-docbook[]\n\nifdef::backend-docbook[]\nifndef::tep-asciidoc-no-roff[]\n# \"unbreak\" docbook-xsl v1.68 for manpages. v1.69 works with or without this.\n# v1.72 breaks with this because it replaces dots not in roff requests.\n[listingblock]\n<example><title>{title}</title>\n<literallayout>\nifdef::doctype-manpage[]\n&#10;.ft C&#10;\nendif::doctype-manpage[]\n|\nifdef::doctype-manpage[]\n&#10;.ft&#10;\nendif::doctype-manpage[]\n</literallayout>\n{title#}</example>\nendif::tep-asciidoc-no-roff[]\n\nifdef::tep-asciidoc-no-roff[]\nifdef::doctype-manpage[]\n# The following two small workarounds insert a simple paragraph after screen\n[listingblock]\n<example><title>{title}</title>\n<literallayout>\n|\n</literallayout><simpara></simpara>\n{title#}</example>\n\n[verseblock]\n<formalpara{id? id=\"{id}\"}><title>{title}</title><para>\n{title%}<literallayout{id? id=\"{id}\"}>\n{title#}<literallayout>\n|\n</literallayout>\n{title#}</para></formalpara>\n{title%}<simpara></simpara>\nendif::doctype-manpage[]\nendif::tep-asciidoc-no-roff[]\nendif::backend-docbook[]\n\nifdef::doctype-manpage[]\nifdef::backend-docbook[]\n[header]\ntemplate::[header-declarations]\n<refentry>\n<refmeta>\n<refentrytitle>{mantitle}</refentrytitle>\n<manvolnum>{manvolnum}</manvolnum>\n<refmiscinfo class=\"source\">libtracefs</refmiscinfo>\n<refmiscinfo class=\"version\">{libtracefs_version}</refmiscinfo>\n<refmiscinfo class=\"manual\">libtracefs Manual</refmiscinfo>\n</refmeta>\n<refnamediv>\n  <refname>{manname1}</refname>\n  <refname>{manname2}</refname>\n  <refname>{manname3}</refname>\n  <refname>{manname4}</refname>\n  <refname>{manname5}</refname>\n  <refname>{manname6}</refname>\n  <refname>{manname7}</refname>\n  <refname>{manname8}</refname>\n  <refname>{manname9}</refname>\n  <refname>{manname10}</refname>\n  <refname>{manname11}</refname>\n  <refname>{manname12}</refname>\n  <refname>{manname13}</refname>\n  <refname>{manname14}</refname>\n  <refname>{manname15}</refname>\n  <refname>{manname16}</refname>\n  <refname>{manname17}</refname>\n  <refname>{manname18}</refname>\n  <refname>{manname19}</refname>\n  <refname>{manname20}</refname>\n  <refname>{manname21}</refname>\n  <refname>{manname22}</refname>\n  <refname>{manname23}</refname>\n  <refname>{manname24}</refname>\n  <refname>{manname25}</refname>\n  <refname>{manname26}</refname>\n  <refname>{manname27}</refname>\n  <refname>{manname28}</refname>\n  <refname>{manname29}</refname>\n  <refname>{manname30}</refname>\n  <refpurpose>{manpurpose}</refpurpose>\n</refnamediv>\nendif::backend-docbook[]\nendif::doctype-manpage[]\n\nifdef::backend-xhtml11[]\n[linktep-inlinemacro]\n<a href=\"{target}.html\">{target}{0?({0})}</a>\nendif::backend-xhtml11[]\n"
  },
  {
    "path": "Documentation/libtracecmd/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\n# Include the utils\ninclude $(src)/scripts/utils.mk\n\n# This Makefile and manpage XSL files were taken from libtracefs\n# and modified for libtracecmd\n\nMAN3_TXT= \\\n\t$(wildcard libtracecmd-*.txt) \\\n\tlibtracecmd.txt\n\nMAN_TXT = $(MAN3_TXT)\n_MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))\n_MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))\n_DOC_MAN3=$(patsubst %.txt,%.m,$(MAN3_TXT))\n\nMAN_XML=$(addprefix $(OUTPUT),$(_MAN_XML))\nMAN_HTML=$(addprefix $(OUTPUT),$(_MAN_HTML))\nDOC_MAN3=$(addprefix $(OUTPUT),$(_DOC_MAN3))\n\n# Make the path relative to DESTDIR, not prefix\nifndef DESTDIR\nprefix?=$(HOME)\nendif\nbindir?=$(prefix)/bin\nhtmldir?=$(prefix)/share/doc/libtracecmd-doc\npdfdir?=$(prefix)/share/doc/libtracecmd-doc\nmandir?=$(prefix)/share/man\nman3dir=$(mandir)/man3\n\nifdef USE_ASCIIDOCTOR\nASCIIDOC_EXTRA += -a mansource=\"libtracecmd\" -a manmanual=\"libtracecmd Manual\"\nendif\n\nall: check-man-tools html man\n\nman: man3\nman3: $(DOC_MAN3)\n\nhtml: $(MAN_HTML)\n\n$(MAN_HTML) $(DOC_MAN3): $(ASCIIDOC_CONF)\n\ninstall: check-man-tools install-man install-html\n\ncheck-man-tools:\nifdef missing_tools\n\t$(error \"You need to install $(missing_tools) for man pages\")\nendif\n\ninstall-%.3: $(OUTPUT)%.3\n\t$(Q)$(call do_install_docs,$<,$(man3dir),644);\n\ndo-install-man: man $(addprefix install-,$(wildcard $(OUTPUT)*.3))\n\ninstall-man: man\n\t$(Q)$(MAKE) -C . do-install-man\n\ninstall-%.txt: $(OUTPUT)%.html\n\t$(Q)$(call do_install_docs,$<,$(htmldir),644);\n\ndo-install-html: html $(addprefix install-,$(wildcard *.txt))\n\ninstall-html: html do-install-html\n\nuninstall: uninstall-man uninstall-html\n\nuninstall-man:\n\t$(Q)$(RM) $(addprefix $(DESTDIR)$(man3dir)/,$(DOC_MAN3))\n\nuninstall-html:\n\t$(Q)$(RM) $(addprefix $(DESTDIR)$(htmldir)/,$(MAN_HTML))\n\nifdef missing_tools\n  DO_INSTALL_MAN = $(warning Please install $(missing_tools) to have the man pages installed)\nelse\n  DO_INSTALL_MAN = do-install-man\nendif\n\nCLEAN_FILES =\t\t\t\t\t\\\n\t$(MAN_XML) $(addsuffix +,$(MAN_XML))\t\\\n\t$(MAN_HTML) $(addsuffix +,$(MAN_HTML))\t\\\n\t$(DOC_MAN3) *.3 *.m\n\nclean:\n\t$(Q) $(RM) $(CLEAN_FILES)\n\nifdef USE_ASCIIDOCTOR\n$(OUTPUT)%.m : $(OUTPUT)%.txt\n\t$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \\\n\t$(ASCIIDOC) -b manpage -d manpage \\\n\t\t$(ASCIIDOC_EXTRA) -alibtracecmd_version=$(LIBTRACECMD_VERSION) -o $@+ $< && \\\n\tmv $@+ $@\nendif\n\n$(OUTPUT)%.m : $(OUTPUT)%.xml\n\t$(QUIET_XMLTO)$(RM) $@ && \\\n\t$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<; \\\n\ttouch $@\n\n$(OUTPUT)%.xml : %.txt\n\t$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \\\n\t$(ASCIIDOC) -b docbook -d manpage \\\n\t\t$(ASCIIDOC_EXTRA) -alibtracecmd_version=$(LIBTRACECMD_VERSION) -o $@+ $< && \\\n\tmv $@+ $@\n\n$(MAN_HTML): $(OUTPUT)%.html : %.txt\n\t$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \\\n\t$(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \\\n\t\t$(ASCIIDOC_EXTRA) -alibtracecmd_version=$(LIBTRACECMD_VERSION) -o $@+ $< && \\\n\tmv $@+ $@\n"
  },
  {
    "path": "Documentation/libtracecmd/install-docs.sh.in",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: LGPL-2.1\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nfor section in 1 3 5; do\n    while IFS= read -r -d '' man; do\n        [ ! -d \"${DESTDIR}@MANDIR@/man${section}\" ] && install -d \"${DESTDIR}@MANDIR@/man${section}\"\n\n        echo Installing \"${man}\" to \"${DESTDIR}@MANDIR@/man${section}\"\n        install -m 0644 \"${man}\" \"${DESTDIR}@MANDIR@/man${section}/\"\n    done< <(find \"@SRCDIR@\" -name \"*\\.${section}\" -type f -print0)\ndone\n\nwhile IFS= read -r -d '' html; do\n    [ ! -d \"${DESTDIR}@HTMLDIR@\"  ] && install -d \"${DESTDIR}@HTMLDIR@\"\n\n    echo Installing \"${html}\" to \"${DESTDIR}@HTMLDIR@\"\n    install -m 0644 \"${html}\" \"${DESTDIR}@HTMLDIR@\"\ndone< <(find \"@SRCDIR@\" -name \"*\\.html\" -type f -print0)\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd-files.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\ntracecmd_open, tracecmd_open_fd, tracecmd_open_head, tracecmd_init_data,\ntracecmd_close, tracecmd_set_private, tracecmd_get_private - Open and close a trace file.\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nstruct tracecmd_input pass:[*]*tracecmd_open*(const char pass:[*]_file_, int _flags_);\nstruct tracecmd_input pass:[*]*tracecmd_open_fd*(int _fd_, int _flags_);\nstruct tracecmd_input pass:[*]*tracecmd_open_head*(const char pass:[*]_file_, int _flags_);\nint *tracecmd_init_data*(struct tracecmd_input pass:[*]_handle_);\nvoid *tracecmd_close*(struct tracecmd_input pass:[*]_handle_);\nvoid *tracecmd_set_private*(struct tracecmd_input pass:[*]_handle_, void pass:[*]_data_);\nvoid pass:[*]*tracecmd_get_private*(struct tracecmd_input pass:[*]_handle_);\n--\n\nDESCRIPTION\n-----------\nThis set of APIs can be used to open and close a trace file recorded by\n*trace-cmd(1)* and containing tracing information from ftrace, the official\nLinux kernel tracer. The opened file is represented by a _tracecmd_input_\nstructure, all other library APIs that work with the file require a pointer\nto the structure. The APIs for opening a trace file have a _flag_ input\nparameter, which controls how the file will be opened and parsed. The _flag_\nis a combination of these options:\n\n TRACECMD_FL_LOAD_NO_PLUGINS - Do not load any plugins\n TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS - Do not load system wide plugins, load only \"local only\"\n\t\t\t\t\tplugins from user's home directory.\n\nThe *tracecmd_open()* function opens a given trace _file_, parses the\nmetadata headers from the file, allocates and initializes а _tracecmd_input_\nhandler structure representing the file. It also initializes the handler\nfor reading trace data from the file. The returned handler is ready to be\nused with _tracecmd_read__ APIs.\n\nThe *tracecmd_open_fd()* function does the same as *tracecmd_open()*, but\nworks with a file descriptor to a trace file, opened for reading.\n\nThe *tracecmd_open_head()* function is the same as *tracecmd_open()*, but\ndoes not initialize the handler for reading trace data. It reads and parses\nthe metadata headers only. The *tracecmd_init_data()* should be used before\nusing the _tracecmd_read__ APIs.\n\nThe *tracecmd_init_data()* function initializes a _handle_, allocated with\n*tracecmd_open_head()*, for reading trace data from the file associated with\nit. This API must be called before any of the _tracecmd_read__ APIs.\n\nThe *tracecmd_close()* function frees a _handle_, pointer to tracecmd_input\nstructure, previously allocated with *tracecmd_open()*, *tracecmd_open_fd()*\nor *tracecmd_open_head()* APIs.\n\nThe *tracecmd_set_private()* function allows to add specific _data_ to the\n_handle_ that can be retrieved later.\n\nThe *tracecmd_get_private()* function will retrieve the _data_ set by\n*tracecmd_set_private()* for the given _handle_.\n\nRETURN VALUE\n------------\nThe *tracecmd_open()*, *tracecmd_open_fd()* and *tracecmd_open_head()*\nfunctions return a pointer to tracecmd_input structure or NULL in case of\nan error. The returned structure must be free with *tracecmd_close()*.\nNote that if *tracecmd_open_fd()* is used to allocate a tracecmd_input handler,\nwhen *tracecmd_close()* is called to close it, that fd will be closed also.\n\nThe *tracecmd_init_data()* function returns -1 in case of an error or\n0 otherwise.\n\nThe *tracecmd_get_private()* returns the _data_ set by *tracecmd_set_private()*.\n\nEXAMPLE\n-------\n[source,c]\n--\nThe are two different use patterns for opening and reading trace data from\na trace file, which can be used depending on the use case.\n\n1. Open and initialise the trace file in а single step:\n\n#include <stdlib.h>\n#include <trace-cmd.h>\n\nstatic int print_events(struct tracecmd_input *handle, struct tep_record *record, int cpu, void *data)\n{\n\tstatic struct trace_seq seq;\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tconst char *file = tracecmd_get_private(handle);\n\n\tif (!seq.buffer)\n\t\ttrace_seq_init(&seq);\n\n\ttrace_seq_reset(&seq);\n\ttrace_seq_printf(&seq, \"%s: \", file);\n\ttep_print_event(tep, &seq, record, \"%6.1000d [%03d] %s-%d %s: %s\\n\",\n\t\t\tTEP_PRINT_TIME, TEP_PRINT_CPU, TEP_PRINT_COMM, TEP_PRINT_PID,\n\t\t\tTEP_PRINT_NAME, TEP_PRINT_INFO);\n\ttrace_seq_terminate(&seq);\n\ttrace_seq_do_printf(&seq);\n\treturn 0;\n}\n\nint main(int argc, char **argv)\n{\n\tstruct tracecmd_input *handle;\n\n\tif (argc < 2) {\n\t\tprintf(\"usage: %s trace.dat\\n\", argv[0]);\n\t\texit(-1);\n\t}\n\n\thandle = tracecmd_open(argv[i], 0);\n\tif (!handle)\n\t\texit(-1);\n\n\ttracecmd_set_private(handles[nr_handles], argv[i]);\n\ttracecmd_iterate_events(handles, NULL, 0, print_events, NULL);\n\n\ttracecmd_close(handle);\n}\n\n2. Open and initialise the trace file in two steps. This allows to perform\nsome processing based on metadata, read from the file, before initialising\nthe trace data for reading. Example for such use case is when opening multiple\ntrace files recorded in a same trace session. In that case timestamps of all\ntrace events must be adjusted based on the information from  the file's metadata\nand before reading the trace data.\n\n#include <trace-cmd.h>\n...\nstruct tracecmd_input *handle = tracecmd_open_head(\"trace.dat\");\n\tif (!handle) {\n\t\t/* Failed to open trace.dat file */\n\t}\n...\n\t/* do some processing, before initialising the trace data for reading */\n...\n\tif (tracecmd_init_data(handle) < 0) {\n\t\t/* Failed to initialize hadle for reading the trace data */\n\t}\n...\n\t/* Read tracing data from the file, using the handle */\n...\n\ttracecmd_close(handle);\n...\n--\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n*libtracefs(3)*,\n*libtraceevent(3)*,\n*trace-cmd(1)*\n*trace-cmd.dat(5)*\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2020 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd-instances.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\ntracecmd_buffer_instances, tracecmd_buffer_instance_name, tracecmd_buffer_instance_handle\n- Read tracing instances from a trace file.\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nint *tracecmd_buffer_instances*(struct tracecmd_input pass:[*]_handle_);\nconst char pass:[*]*tracecmd_buffer_instance_name*(struct tracecmd_input pass:[*]_handle_, int _indx_);\nstruct tracecmd_input pass:[*]*tracecmd_buffer_instance_handle*(struct tracecmd_input pass:[*]_handle_, int _indx_);\n--\n\nDESCRIPTION\n-----------\nThis set of APIs can be used to get information and read tracing data\nfrom tracing instances stored in a trace file.\n\nThe *tracecmd_buffer_instances()* function gets the number of tracing\ninstances recorded in a trace file. The top instance is not counted.\nThe _handle_ is a tracecmd_input handler returned by\n*tracecmd_open_head()*.\n\nThe *tracecmd_buffer_instance_name()* function gets the name of the\ntracing instance with given index _indx_, recorded in a trace file.\nThe _indx_ is a number in the interval [0 .. count-1], where count\nis the number returned by *tracecmd_buffer_instances()*. The _handle_\nis a tracecmd_input handler returned by *tracecmd_open_head()*.\n\nThe *tracecmd_buffer_instance_handle()* allocates and initializes a\ntracecmd_input handle, associated with trace instance with index\n_indx_ from a trace file.  The _handle_ is a tracecmd_input handler\nreturned by *tracecmd_open_head()*. The _indx_ is a number in the\ninterval [0 .. count-1], where count is the number returned by\n*tracecmd_buffer_instances()*.\n\nRETURN VALUE\n------------\nThe *tracecmd_buffer_instances()* function returns the number of tracing\ninstances recorded in a trace file.\n\nThe *tracecmd_buffer_instance_name()* function returns a string, the name\nof a tracing instance, or NULL in case of an error The string must *not*\nbe freed.\n\nThe *tracecmd_buffer_instance_handle()* function returns a pointer to\nnewly allocated tracecmd_input handler or NULL in case if an error. The\nreturned handler must be closed by *tracecmd_close()(3)*\n\nEXAMPLE\n-------\n[source,c]\n--\n#include <trace-cmd.h>\n...\nstruct tracecmd_input *handle = tracecmd_open_head(\"trace.dat\");\n\tif (!handle) {\n\t\t/* Failed to open trace.dat file */\n\t}\n...\nint num = tracecmd_buffer_instances(handle);\n\n\twhile(num) {\n\t\tstruct tracecmd_input *h;\n\t\tchar *name;\n\n\t\tname = tracecmd_buffer_instance_name(handle, num);\n\t\tif (!name) {\n\t\t\t/* Failed to get name of instance num */\n\t\t}\n\t\th = tracecmd_buffer_instance_handle(handle, num);\n\t\tif (!h) {\n\t\t\t/* Failed to initialize handler for instance num */\n\t\t}\n\n\t\t...\n\t\ttracecmd_close(h);\n\t\tnum--;\n\t}\n...\n\ttracecmd_close(handle);\n\n--\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n*libtracefs(3)*,\n*libtraceevent(3)*,\n*trace-cmd(1)*\n*trace-cmd.dat(5)*\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2020 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd-iterate.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\ntracecmd_iterate_events, tracecmd_iterate_events_multi, tracecmd_follow_event,\ntracecmd_follow_missed_events, tracecmd_filter_add, tracecmd_iterate_reset - Read events from a trace file\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nint *tracecmd_iterate_events*(struct tracecmd_input pass:[*]_handle_,\n\t\t\t    cpu_set_t pass:[*]_cpus_, int _cpu_size_,\n\t\t\t    int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t    struct tep_record pass:[*],\n\t\t\t\t\t    int, void pass:[*]),\n\t\t\t    void pass:[*]_callback_data_);\nint *tracecmd_iterate_events_multi*(struct tracecmd_input pass:[**]_handles_,\n\t\t\t\t  int _nr_handles_,\n\t\t\t\t  int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t\t\t   struct tep_record pass:[*],\n\t\t\t\t\t\t\t   int, void pass:[*]),\n\t\t\t\t  void pass:[*]_callback_data_);\nint *tracecmd_iterate_events_reverse*(struct tracecmd_input pass:[*]_handle_,\n\t\t\t    cpu_set_t pass:[*]_cpus_, int _cpu_size_,\n\t\t\t    int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t    struct tep_record pass:[*],\n\t\t\t\t\t    int, void pass:[*]),\n\t\t\t    void pass:[*]_callback_data_, bool _cont_);\nint *tracecmd_follow_event*(struct tracecmd_input pass:[*]_handle_,\n\t\t\t  const char pass:[*]_system_, const char pass:[*]_event_name_,\n\t\t\t  int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t  struct tep_event pass:[*],\n\t\t\t\t\t  struct tep_record pass:[*],\n\t\t\t\t\t  int, void pass:[*]),\n\t\t\t  void pass:[*]_callback_data_);\nint *tracecmd_follow_missed_events*(struct tracecmd_input pass:[*]_handle_,\n\t\t\t\t   int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t\t   struct tep_event pass:[*],\n\t\t\t\t\t\t   struct tep_record pass:[*],\n\t\t\t\t\t\t   int, void pass:[*]),\n\t\t\t\t   void pass:[*]_callback_data_);\nstruct tracecmd_filter pass:[*]*tracecmd_filter_add*(struct tracecmd_input *_handle_,\n\t\t\t\t\t    const char pass:[*]_filter_str_, bool _neg_);\nint *tracecmd_iterate_reset*(struct tracecmd_input pass:[*]_handle_);\n--\n\nDESCRIPTION\n-----------\nThis set of APIs can be used to iterate over events after opening a trace file\nusing one of the open functions like *tracecmd_open(3)* or *tracecmd_open_fd(3)*.\n\nThe function *tracecmd_iterate_events()* will iterate through all the\nevents in the trace file defined by _handle_, where _handle_ is returned from\none of the *tracecmd_open(3)* functions. It will call the _callback_() function\non the events on the CPUs defined by _cpus_. The _cpu_size_ must be the size of\n_cpus_ (see *CPU_SET(3)*). If _cpus_ is NULL, then _cpu_size_ is ignored and _callback()_\nwill be called for all events on all CPUs in the trace file. The _callback_data_\nis passed to the _callback()_ as its last parameter. _callback_ may be NULL, which\nis useful if *tracecmd_follow_event()* is used, but note if _callback_ is NULL, then\n_callback_data_ is ignored and not sent to the _callback_ of *tracecmd_follow_event()*.\n\nThe function *tracecmd_iterate_events_multi()* is similar to *tracecmd_iterate_events()*\nexcept that it allows to iterate over more than one trace file. If *tracecmd agent(1)*\nis used to get a trace file for both the host and guest, make sure that the host trace\nfile is the first entry in _handles_ and *tracecmd_iterate_events_multi()* will do\nthe synchronization of the meta data for the guest files that come later in _handles_.\n_handles_ is an array of trace file descriptors that were opened by *tracecmd_open(3)*\nand friends. Note, unlike *tracecmd_iterate_events()*, *tracecmd_iterate_events_multi()*\ndoes not filter on CPUs, as it will cause the API to become too complex in knowing which\nhandle to filter the CPUs on. If CPU filtering is desired, then the _callback_ should check\nthe _record_->cpu to and return 0 if it is not the desired CPU to process. _nr_handles_\ndenotes the number of elements in _handles_. The _callback_data_ is passed to the _callback_\nas its last parameter.  _callback_ may be NULL, which is useful if *tracecmd_follow_event()*\nis used, but note if _callback_ is NULL, then _callback_data_ is ignored and not sent to the\n_callback_ of *tracecmd_follow_event()*.\n\nThe function *tracecmd_iterate_events_reverse()* works pretty much the same way as\n*tracecmd_iterate_events()* works, but instead of calling the _callback_() function for\neach event in order of the timestamp, it will call the _callback_() function for\neach event in reverse order of the timestamp. If _cont_ is false, it will start by\ncalling the event with the oldest timestamp in the trace.dat file. If _cont_ is set\nto true, then it will start whereever the current position of the tracing data is.\nFor instance, if the _callback()_ return something other than zero it will exit the\niteration. If *tracecmd_iterate_events_reverse()* is called again with _cont_ to true\nit will continue where it left off. If _cont_ is false, it will start again at the event\nwith the oldest timestamp. The _handle_, _cpus_, _cpu_size_, and _callback_data_ act the\nsame as *tracecmd_iterate_events()*.\n\nThe _callback()_ for both *tracecmd_iterate_events()*, *tracecmd_iterate_events_reverse()*\nand *tracecmd_iterate_events_multi()* is of the prototype:\n\nint _callback()_(struct tracecmd_input pass:[*]_handle_, struct tep_record pass:[*]_record_,\n\t\t int _cpu_, void pass:[*]_data_);\n\nThe _handle_ is the same _handle_ passed to *tracecmd_iterate_events()* or the current\nhandle of _handles_ passed to *tracecmd_iterate_events_multi()* that the _record_ belongs to.\nThe _record_ is the current event record. The _cpu_ is the current CPU being processed. Note, for\n*tracecmd_iterate_events_multi()* it may not be the actual CPU of the file, but the nth\nCPU of all the _handles_ put together. Use _record_->cpu to get the actual CPU that the\nevent is on.\n\nThe *tracecmd_follow_event()* function will attach to a trace file descriptor _handle_\nand call the _callback_ when the event described by _system_ and _name_ matches an event\nin the iteration of *tracecmd_iterate_events()* or *tracecmd_iterate_events_multi()*.\nNote, the _cpu_ is the nth CPU for both *tracecmd_iterate_events()* and\n*tracecmd_iterate_events_multi()*. If the actual CPU of the _record_ is needed, use\n_record_->cpu.\nFor *tracecmd_iterate_events_multi()*, the _callback_ is only called if the _handle_\nmatches the current trace file descriptor within _handles_. The _callback_data_ is\npassed as the last parameter to the _callback()_ function. Note, this _callback()_\nfunction will be called before the _callback()_ function of either *tracecmd_iterate_events()* or *tracecmd_iterate_events_multi()*.\n\nThe _callback()_ prototype for *tracecmd_follow_event()_ is:\n\nint _callback()_(struct tracecmd_input pass:[*]_handle_, struct tep_event pass:[*]_event,\n\t\t struct tep_record pass:[*]_record_, int _cpu_, void pass:[*]_data_);\n\nThe *tracecmd_follow_missed_events()* function will attach to a trace file descriptor\n_handle_ and call the _callback_ when missed events are detected. The _event_ will\nhold the type of event that the _record_ is. The _record_ will hold the information\nof the missed events. The _cpu_ is the nth CPU for both *tracecmd_iterate_events()*\nand *tracecmd_iterate_events_multi()*. If the CPU that the missed events are for is\nneeded, use _record_->cpu. If _record_->missed_events is a positive number, then it\nholds the number of missed events since the last event on its CPU, otherwise it\nwill be negative, and that will mean that the number of missed events is unknown but\nmissed events exist since the last event on the CPU.\nThe _callback_ and _callback_data_ is the same format as *tracecmd_follow_event()* above.\nThe missed events _callback_ is called before any of the other _callbacks_ and any\nfilters that were added by *tracecmd_filter_add()* are ignored.\nIf _callback_ returns a non zero, it will stop the iterator before it calls any of the\nother iterator callbacks for the given record.\n\nThe *tracecmd_filter_add()* function, adds a filter to _handle_ that affects\nboth *tracecmd_iterate_events()* and *tracecmd_iterate_events_multi()*.\nThe _filter_str_ is a character string defining a filter in a format that\nis defined by *tep_filter_add_filter_str(3)*. If _neg_ is true, then the events\nthat match the filter will be skipped, otherwise the events that match will execute\nthe _callback()_ function in the iterators.\n\nThe *tracecmd_iterate_reset()* sets the _handle_ back to start at the beginning, so that\nthe next call to *tracecmd_iterate_events()* starts back at the first event again, instead\nof continuing where it left off.\n\nRETURN VALUE\n------------\nBoth *tracecmd_iterate_events()*, *tracecmd_iterate_events_reverse()* and\n*tracecmd_iterate_events_multi()* return zero if they successfully iterated all events\n(handling the follow and filters appropriately). Or an error value, which can include\nreturning a non-zero result from the _callback()_ function.\n\n*tracecmd_iterate_reset()* returns 0 on success and -1 if an error occurred. Note,\nif -1 is returned, a partial reset may have also happened.\n\nEXAMPLE\n-------\n[source,c]\n--\n#define _GNU_SOURCE\n#include <sched.h>\n#include <stdlib.h>\n#include <getopt.h>\n#include <trace-cmd.h>\n\nstruct private_data {\n\tint\t\tcpu;\n\tconst char\t*file;\n};\n\nstatic int print_events(struct tracecmd_input *handle, struct tep_record *record, int cpu, void *data)\n{\n\tstatic struct trace_seq seq;\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tstruct private_data *pdata = tracecmd_get_private(handle);\n\n\t/* For multi handles we need this */\n\tif (pdata->cpu >= 0 && pdata->cpu != record->cpu)\n\t\treturn 0;\n\n\tif (!seq.buffer)\n\t\ttrace_seq_init(&seq);\n\n\ttrace_seq_reset(&seq);\n\ttrace_seq_printf(&seq, \"%s: \", pdata->file);\n\ttep_print_event(tep, &seq, record, \"%6.1000d [%03d] %s-%d %s: %s\\n\",\n\t\t\tTEP_PRINT_TIME, TEP_PRINT_CPU, TEP_PRINT_COMM, TEP_PRINT_PID,\n\t\t\tTEP_PRINT_NAME, TEP_PRINT_INFO);\n\ttrace_seq_terminate(&seq);\n\ttrace_seq_do_printf(&seq);\n\treturn 0;\n}\n\nstatic int print_event(struct tracecmd_input *handle, struct tep_event *event,\n\t\t       struct tep_record *record, int cpu, void *data)\n{\n\treturn print_events(handle, record, cpu, data);\n}\n\nstatic int missed_events(struct tracecmd_input *handle, struct tep_event *event,\n\t\t\t struct tep_record *record, int cpu, void *data)\n{\n\tif (record->missed_events > 0)\n\t\tprintf(\"CPU [%03d] has %d missed events\\n\",\n\t\t\t record->cpu, record->missed_events);\n\telse\n\t\tprintf(\"CPU [%03d] has missed events\\n\", record->cpu);\n\treturn 0;\n}\n\nstatic void usage(const char *argv0)\n{\n\tprintf(\"usage: [-c cpu][-f filter][-e event] %s trace.dat [trace.dat ...]\\n\",\n\t       argv0);\n\texit(-1);\n}\n\nint main(int argc, char **argv)\n{\n\tstruct tracecmd_input **handles = NULL;\n\tconst char *filter_str = NULL;\n\tconst char *argv0 = argv[0];\n\tstruct private_data *priv;\n\tcpu_set_t *cpuset = NULL;\n\tchar *event = NULL;\n\tsize_t cpusize = 0;\n\tint nr_handles = 0;\n\tint cpu = -1;\n\tint i;\n\tint c;\n\n\twhile ((c = getopt(argc, argv, \"c:f:e:\")) >= 0) {\n\t\tswitch (c) {\n\t\tcase 'c':\n\t\t\t/* filter all trace data to this one CPU. */\n\t\t\tcpu = atoi(optarg);\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tfilter_str = optarg;\n\t\t\tbreak;\n\t\tcase 'e':\n\t\t\tevent = optarg;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv0);\n\t\t}\n\t}\n\targc -= optind;\n\targv += optind;\n\n\tif (argc == 0)\n\t\tusage(argv0);\n\n\tfor (i = 0; i < argc; i++) {\n\t\thandles = realloc(handles, sizeof(*handles) * (nr_handles + 1));\n\t\tif (!handles)\n\t\t\texit(-1);\n\t\thandles[nr_handles] = tracecmd_open(argv[i], 0);\n\t\tif (!handles[nr_handles]) {\n\t\t\tperror(argv[i]);\n\t\t\texit(-1);\n\t\t}\n\t\tif (filter_str) {\n\t\t\tif (tracecmd_filter_add(handles[nr_handles], filter_str, false) == NULL) {\n\t\t\t\tperror(\"adding filter\");\n\t\t\t\texit(-1);\n\t\t\t}\n\t\t}\n\t\tpriv = calloc(1, sizeof(*priv));\n\t\tif (!priv)\n\t\t\texit(-1);\n\t\tpriv->file = argv[i];\n\t\tpriv->cpu = cpu;\n\t\ttracecmd_set_private(handles[nr_handles], priv);\n\t\tif (event) {\n\t\t\tif (tracecmd_follow_event(handles[nr_handles], NULL, event, print_event, NULL) < 0) {\n\t\t\t\tprintf(\"Could not follow event %s for file %s\\n\", event, argv[i]);\n\t\t\t\texit(-1);\n\t\t\t}\n\t\t}\n\t\ttracecmd_follow_missed_events(handles[nr_handles], missed_events, NULL);\n\t\tnr_handles++;\n\t}\n\n\t/* Shortcut */\n\tif (nr_handles == 1) {\n\t\tif (cpu >= 0) {\n\t\t\tcpuset = CPU_ALLOC(cpu + 1);\n\t\t\tif (!cpuset)\n\t\t\t\texit(-1);\n\t\t\tcpusize = CPU_ALLOC_SIZE(cpu + 1);\n\t\t\tCPU_SET_S(cpu, cpusize, cpuset);\n\t\t}\n\t\tif (event)\n\t\t\ttracecmd_iterate_events(handles[0], cpuset, cpusize, NULL, NULL);\n\t\telse\n\t\t\ttracecmd_iterate_events(handles[0], cpuset, cpusize, print_events, NULL);\n\t} else {\n\t\tif (event)\n\t\t\ttracecmd_iterate_events_multi(handles, nr_handles, NULL, NULL);\n\t\telse\n\t\t\ttracecmd_iterate_events_multi(handles, nr_handles, print_events, NULL);\n\t}\n\n\tfor (i = 0; i < nr_handles; i++) {\n\t\tpriv = tracecmd_get_private(handles[i]);\n\t\tfree(priv);\n\t\ttracecmd_close(handles[i]);\n\t}\n\tfree(handles);\n}\n--\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n*libtracefs(3)*,\n*libtraceevent(3)*,\n*trace-cmd(1)*\n*trace-cmd.dat(5)*\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2020 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd-log.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\ntracecmd_set_loglevel - Set log level of the library\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nint *tracecmd_set_loglevel*(enum tep_loglevel _level_);\n--\n\nDESCRIPTION\n-----------\nThe *tracecmd_set_loglevel()* function sets the level of the library logs that will be printed on\nthe console. See *libtraceevent(3)* for detailed desciription of the log levels. Setting the log\nlevel to specific value means that logs from the previous levels will be printed too. For example\n_TEP_LOG_WARNING_ will print any logs with severity _TEP_LOG_WARNING_, _TEP_LOG_ERROR_ and\n_TEP_LOG_CRITICAL_. The default log level is _TEP_LOG_CRITICAL_.  When a new level is set, it is\nalso propagated to the libtracefs and libtraceevent.\n\nEXAMPLE\n-------\n[source,c]\n--\n#include <trace-cmd.h>\n...\ntracecmd_set_loglevel(TEP_LOG_ALL);\n...\n/* call libtracecmd, libtracefs or libtraceevent APIs and observe any logs they produce */\n...\ntracecmd_set_loglevel(TEP_LOG_CRITICAL);\n--\n\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n*libtracefs(3)*,\n*libtraceevent(3)*,\n*trace-cmd(1)*\n*trace-cmd.dat(5)*\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2021 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd-maps.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\ntracecmd_map_vcpus, tracecmd_get_cpu_map, tracecmd_map_find_by_host_pid, tracecmd_map_get_host_pid,\ntracecmd_map_get_guest, tracecmd_map_set_private, tracecmd_map_get_private - Mapping host and guest data\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nint *tracecmd_map_vcpus*(struct tracecmd_input pass:[**]handles, int nr_handles);\nstruct tracecmd_cpu_map pass:[*]*tracecmd_get_cpu_map*(struct tracecmd_input pass:[*]handle, int cpu);\nstruct tracecmd_cpu_map pass:[*]*tracecmd_map_find_by_host_pid*(struct tracecmd_input pass:[*]handle,\n\t\t\t\t\t\t      int host_pid);\nint *tracecmd_map_get_host_pid*(struct tracecmd_cpu_map pass:[*]map);\nstruct tracecmd_input pass:[*]*tracecmd_map_get_guest*(struct tracecmd_cpu_map pass:[*]map);\nvoid *tracecmd_map_set_private*(struct tracecmd_cpu_map pass:[*]map, void pass:[*]priv);\nvoid pass:[*]*tracecmd_map_get_private*(struct tracecmd_cpu_map pass:[*]map);\n--\n\nDESCRIPTION\n-----------\nThis set of APIs is used to map host and guest trace files for to facilitate\nfurther tracing analysis.\n\nThe *tracecmd_map_vcpus()* takes an array of _handles_ where each item in that\narray was created by one of the *tracecmd_open(3)* functions, and the number\nof handles as _nr_handles_. The first handle in the array of _handles_ is expected\nto be the descriptor for the host tracing file, and the rest are guest trace\nfiles that run on the host, and were created by the *trace-cmd record(1)* and\n*trace-cmd agent(1)* interactions. It returns the number of guests found in\n_handles_ that were associated with the host, or negative on error.\n\nThe *tracecmd_get_cpu_map()* returns a descriptor for a given CPU for a handle.\nIf the _handle_ was a guest defined from *tracecmd_map_vcpus()* then the mapping\ncreated from that function that is associated to this particular vCPU (denoted by\n_cpu_) from _handle_. This destriptor can be used by *tarcecmd_map_get_guest()*,\n*tracecmd_map_set_private()* and *tracecmd_map_get_private()* functions.\n\nThe *tracecmd_map_find_by_host_pid()* will return a mapping for a guest virtual\nCPU that is handled by the given _host_pid_. Note, the _handle_ passed in can be\neither the host handle or one of the guest's handles for that host that was\nmapped by *tracecmd_map_vcpus()*, even if the guest handle does not have the vCPU\nthat the _host_pid_ represents.\n\nThe *tracecmd_map_get_host_pid()* will recturn the host_pid for a given _map_\nthat was retrieved by one of the above functions.\n\nThe *tracecmd_map_get_guest()* will recturn the guest_handle for a given _map_\nthat was retrieved by one of the above functions.\n\nThe *tracecmd_map_set_private()* allows the application to assign private data\nfor a given guest vCPU to host thread mapping defined by _map_.\n\nThe *tracecmd_map_get_private()* retrieves the _priv_ data from _map_ that was\nset by *tracecmd_map_set_private()*.\n\nRETURN VALUE\n------------\n*tracecmd_map_vcpus()* returns the number of guests in the _handles_ array that\nwere mapped to the host handle that is the first entry in _handles_. It returns\n-1 on error.\n\n*tracecmd_get_cpu_map()* returns a map created by *tracecmd_map_vcpus()* for\na given _cpu_ for a given _handle_, or NULL if it is not found.\n\n*tracecmd_map_find_by_host_pid()* returns a map that is associated by the host\ntask with _host_pid_ as its process ID. _handle_ can be either a the host\nhandle, or one of the guest handles that were mapped to the host via\n*tracecmd_map_vcpus()*, even if the guest handle is another guest than\nthe one that the mapping is for. It returns NULL if not found.\n\n*tracecmd_map_get_host_pid()* returns the host process ID for an associated\nmapping defined by _map_.\n\n*tracecmd_map_get_guest()* returns the guest handle for an associated\nmapping defined by _map_.\n\n*tracecmd_map_get_private()* returns the private data of a mapping defined\nby _map_ that was set by *tracecmd_map_set_private()*.\n\nEXAMPLE\n-------\n[source,c]\n--\n#include <stdlib.h>\n#include <errno.h>\n#include <trace-cmd.h>\n\nint main(int argc, char **argv)\n{\n\tstruct tracecmd_input **handles = NULL;\n\tint nr_handles;\n\tint i;\n\n\tif (argc < 2) {\n\t\tprintf(\"usage: host_trace.dat guest1_trace.dat [guest2_trace.dat ...]\\n\");\n\t\texit(-1);\n\t}\n\n\tfor (i = 1; i < argc; i++) {\n\t\thandles = realloc(handles, sizeof(*handles) * (nr_handles + 1));\n\t\tif (!handles)\n\t\t\texit(-1);\n\t\thandles[nr_handles] = tracecmd_open(argv[i], 0);\n\t\tif (!handles[nr_handles]) {\n\t\t\tperror(argv[1]);\n\t\t\texit(-1);\n\t\t}\n\t\ttracecmd_set_private(handles[nr_handles], argv[i]);\n\t\tnr_handles++;\n\t}\n\n\ttracecmd_map_vcpus(handles, nr_handles);\n\n\tfor (i = 1; i < nr_handles; i++) {\n\t\tstruct tracecmd_cpu_map *map;\n\t\tstruct tep_handle *tep;\n\t\tconst char *file = tracecmd_get_private(handles[i]);\n\t\tint cpus, cpu;\n\n\t\tprintf(\"Mappings for guest %s:\\n\", file);\n\t\ttep = tracecmd_get_tep(handles[i]);\n\t\tcpus = tep_get_cpus(tep);\n\t\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\t\tprintf(\"  [%03d] \", cpu);\n\t\t\tmap = tracecmd_get_cpu_map(handles[i], cpu);\n\t\t\tif (!map) {\n\t\t\t\tprintf(\"Has no mapping!\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tprintf(\"host_pid: %d\\n\", tracecmd_map_get_host_pid(map));\n\t\t}\n\t}\n\tfor (i = 0; i < nr_handles; i++)\n\t\ttracecmd_close(handles[i]);\n\tfree(handles);\n\texit(0);\n}\n--\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n*libtracefs(3)*,\n*libtraceevent(3)*,\n*trace-cmd(1)*\n*trace-cmd.dat(5)*\n\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2020 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd-peer.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\ntracecmd_get_traceid, tracecmd_get_guest_cpumap - Manage trace session with multiple trace peers,\nrecorded in multiple trace files.\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nunsigned long long *tracecmd_get_traceid*(struct tracecmd_input pass:[*]_handle_);\nint *tracecmd_get_guest_cpumap*(struct tracecmd_input pass:[*]_handle_, unsigned long long _trace_id_, const char pass:[*]pass:[*]_name_, int pass:[*]_vcpu_count_, const int pass:[*]pass:[*]_cpu_pid_);\n--\n\nDESCRIPTION\n-----------\nThis set of APIs can be used to manage a trace session with multiple trace\npeers, for example, tracing both a host and one or more guest virtual machines.\nThe trace data of each peer from the session is recorded in separate trace files.\nInformation about peers from the session is stored in the metadata of each\ntrace file. These APIs use that information to extract and synchronize\nthe trace data.\n\nThe *tracecmd_get_traceid()* function returns the trace ID stored in\nthe trace file metadata associated with _handle_. Each peer from a trace\nsession has an ID unique for that peer and that trace session only.\nThis ID is used to match multiple trace files recorded in a same trace\nsession.\n\nThe *tracecmd_get_guest_cpumap()* function gets the mapping of guest\nvirtual CPUs (VCPU) to the host process that represents those VCPUs and is\nstored in the metadata of the trace file associated with _handle_. This\ninformation is gathered during a host-guest trace session and is stored\nin the host trace file. The _trace_id_ parameter is the trace ID of the guest\nin this particular trace session. If a guest with that ID was part of that\nsession, its VCPU to host process mapping is in the host trace file and the\ninformation is returned in _name_, _vcpu_count_ and _cpu_pid_ parameters.\nThe _name_ parameter contains the name of the guest, the _vcpu_count_ contains\nthe count of VCPUs of that guest and the  _cpu_pid_ array contains the VCPU to\nhost process mapping. The array is of size _vcpu_count_ where the index is VCPU\nand the value is the process ID (PID) of the host process, running that VCPU.\nThe _name_, _vcpu_count_ and _cpu_pid_ values must *not* be freed.\n\nRETURN VALUE\n------------\nThe *tracecmd_get_traceid()* function returns a 64 bit trace ID.\n\nThe *tracecmd_get_guest_cpumap()* function returns -1 in case of\nan error or 0 otherwise. If 0 is returned, then the _name_, _vcpu_count_\nand _cpu_pid_ parameters contain the requested information.\n\nEXAMPLE\n-------\n[source,c]\n--\n#include <trace-cmd.h>\n...\nstruct tracecmd_input *host = tracecmd_open(\"trace.dat\");\n\tif (!host) {\n\t\t/* Failed to open host trace file */\n\t}\n\nstruct tracecmd_input *guest1 = tracecmd_open_head(\"trace-Guest1.dat\");\n\tif (!guest1) {\n\t\t/* Failed to open guest1 trace file */\n\t}\nstruct tracecmd_input *guest2 = tracecmd_open_head(\"trace-Guest2.dat\");\n\tif (!guest2) {\n\t\t/* Failed to open guest2 trace file */\n\t}\n\nunsigned long long guest_id_1 = tracecmd_get_traceid(guest1);\nunsigned long long guest_id_2 = tracecmd_get_traceid(guest2);\nint *cpu_pid_1, *cpu_pid_2;\nint vcount_1, vcount_2;\nchar *name_1, *name_2;\n\n\tif (!tracecmd_get_guest_cpumap(host, guest_id_1, &name_1, &vcount_1, &cpu_pid_1)) {\n\t\t/* The Host and a guest1 with name_1 are part of the same trace session.\n\t\t * Got guest1 VCPU to host PID mapping.\n\t\t */\n\t}\n\tif (!tracecmd_get_guest_cpumap(host, guest_id_2, &name_2, &vcount_2, &cpu_pid_2)) {\n\t\t/* The Host and a guest2 with name_2 are part of the same trace session.\n\t\t * Got guest2 VCPU to host PID mapping.\n\t\t */\n\t}\n...\n\ttracecmd_close(guest1);\n\ttracecmd_close(guest2);\n\ttracecmd_close(handle);\n\n--\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n*libtracefs(3)*,\n*libtraceevent(3)*,\n*trace-cmd(1)*\n*trace-cmd.dat(5)*\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2020 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd-record.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\ntracecmd_read_cpu_first, tracecmd_read_data, tracecmd_read_at,\ntracecmd_free_record, tracecmd_get_tep - Read recorded events from a trace file.\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nstruct tep_record pass:[*]*tracecmd_read_cpu_first*(struct tracecmd_input pass:[*]_handle_, int _cpu_);\nstruct tep_record pass:[*]*tracecmd_read_data*(struct tracecmd_input pass:[*]_handle_, int _cpu_);\nstruct tep_record pass:[*]*tracecmd_read_at*(struct tracecmd_input pass:[*]_handle_, unsigned long long _offset_, int pass:[*]_cpu_);\nvoid *tracecmd_free_record*(struct tep_record pass:[*]_record_);\nstruct tep_handle pass:[*]*tracecmd_get_tep*(struct tracecmd_input pass:[*]_handle_);\n--\n\nDESCRIPTION\n-----------\nThis set of APIs can be used to read tracing data from a trace file opened\nwith *tracecmd_open()(3)*, *tracecmd_open_fd()(3)* or *tracecmd_open_head()(3)*.\n\nThe *tracecmd_read_cpu_first()* function reads the first trace record\nfor a given _cpu_ from a trace file associated with _handle_. The returned\nrecord must be freed with *tracecmd_free_record()*.\n\nThe *tracecmd_read_data()* function reads the next trace record for\na given _cpu_ from a trace file associated with _handle_ and increments\nthe read location pointer, so that the next call to *tracecmd_read_data()*\nwill not read the same record again. The returned record must be freed\nwith *tracecmd_free_record()*.\n\nThe *tracecmd_read_at()* function reads a trace record from a specific\n_offset_ within the file associated with _handle_. The CPU on which the\nrecorded event occurred is stored in the _cpu_. The function does not\nchange the current read location pointer. The returned record must be\nfreed with *tracecmd_free_record()*.\n\nThe *tracecmd_free_record()* function frees a _record_ returned by any\nof the _tracecmd_read__ APIs.\n\nThe *tracecmd_get_tep()* function returns a tep context for a given\n_handle_.\n\nRETURN VALUE\n------------\nThe *tracecmd_read_cpu_first()*, *tracecmd_read_data()* and\n*tracecmd_read_at()* functions return a pointer to struct tep_record or\nNULL in case of an error.The returned record must be freed with\n*tracecmd_free_record()*.\n\nThe *tracecmd_get_tep()* function returns a pointer to tep context or\nNULL if there is no tep context for the given _handle_. The returned\ntep pointer must *not* be freed.\n\nEXAMPLE\n-------\n[source,c]\n--\n#include <trace-cmd.h>\n...\nstruct tracecmd_input *handle = tracecmd_open(\"trace.dat\");\n\tif (!handle) {\n\t\t/* Failed to open trace.dat file */\n\t}\n...\nunsigned long long offset = 0;\nstruct tep_record *rec;\nint cpu = 0;\n\trec = tracecmd_read_cpu_first(handle, cpu);\n\twhile (rec) {\n\t\t...\n\t\tif ( /* some interesting record noticed */) {\n\t\t\t/* store the offset of the interesting record */\n\t\t\toffset = rec->offset;\n\t\t}\n\t\t...\n\t\ttracecmd_free_record(rec);\n\t\trec = tracecmd_read_data(handle, cpu);\n\t}\n\t...\n\tif (offset) {\n\t\trec = tracecmd_read_at(handle, offset, &cpu);\n\t\tif (rec) {\n\t\t\t/* Got record at offset on cpu */\n\t\t\t...\n\t\t\ttracecmd_free_record(rec);\n\t\t}\n\t}\n\n...\n\ttracecmd_close(hadle);\n\n--\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n*libtracefs(3)*,\n*libtraceevent(3)*,\n*trace-cmd(1)*\n*trace-cmd.dat(5)*\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2020 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd-timestamp.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\ntracecmd_get_first_ts, tracecmd_add_ts_offset, tracecmd_get_tsc2nsec - Handle time stamps from a trace file.\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nunsigned long long *tracecmd_get_first_ts*(struct tracecmd_input pass:[*]_handle_);\nvoid *tracecmd_add_ts_offset*(struct tracecmd_input pass:[*]_handle_, long long _offset_);\nint *tracecmd_get_tsc2nsec*(struct tracecmd_input pass:[*]_handle_, int pass:[*]_mult_, int pass[*]_shift_, unsigned long long pass:[*]_offset_);\n--\n\nDESCRIPTION\n-----------\nThis set of APIs can be used to read tracing data from a trace file opened\nwith _tracecmd_open()(3)_, _tracecmd_open_fd()(3)_ or _tracecmd_open_head()(3)_.\n\nThe *tracecmd_get_first_ts()* function returns the time stamp of the first\nrecord in the _handle_.\n\nThe *tracecmd_add_ts_offset()* function adds an offset to each of the records\nin the _handle_ that represents a trace file. This is useful for associating two\ndifferent tracing files by their offset (for example a trace file from a host\nand a trace file from a guest that were not synchronized when created).\n\nThe *tracecmd_get_tsc2nsec* returns the calculation values that convert the\nraw timestamp into nanoseconds. The parameters are pointers to the storage to save\nthe values, or NULL to ignore them. The multiplier will be saved in _mult_, the\nshift value will be saved in _shift_, and the offset value will be saved in\n_offset_, if the corresponding parameters are not NULL.\n\nRETURN VALUE\n------------\nThe *tracecmd_get_first_ts()* function returns the timestamp of the first\nrecord in a trace file for the given _handle_.\n\nThe *tracecmd_get_tsc2nsec*() returns 0 if the tracing clock supports the\nshift values and -1 otherwise. Note, that if the trace file has the TSC2NSEC\noption, the values returned in the parameters may still be valid even if the\nfunction itself returns -1. The return value only notes if the values will\nbe used in the calculations of the given clock.\n\nEXAMPLE\n-------\n[source,c]\n--\n#include <stdlib.h>\n#include <trace-cmd.h>\n\nstatic int print_events(struct tracecmd_input *handle, struct tep_record *record, int cpu, void *data)\n{\n\tstatic struct trace_seq seq;\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tconst char *file = tracecmd_get_private(handle);\n\n\tif (!seq.buffer)\n\t\ttrace_seq_init(&seq);\n\n\ttrace_seq_reset(&seq);\n\ttrace_seq_printf(&seq, \"%s: \", file);\n\ttep_print_event(tep, &seq, record, \"%6.1000d [%03d] %s-%d %s: %s\\n\",\n\t\t\tTEP_PRINT_TIME, TEP_PRINT_CPU, TEP_PRINT_COMM, TEP_PRINT_PID,\n\t\t\tTEP_PRINT_NAME, TEP_PRINT_INFO);\n\ttrace_seq_terminate(&seq);\n\ttrace_seq_do_printf(&seq);\n\treturn 0;\n}\n\nint main(int argc, char **argv)\n{\n\tstruct tracecmd_input **handles = NULL;\n\tunsigned long long ts, first_ts = 0;\n\tunsigned long long offset;\n\tint multi;\n\tint shift;\n\tint nr_handles = 0;\n\tint ret;\n\tint i;\n\n\tif (argc < 2) {\n\t\tprintf(\"usage: %s trace.dat [trace.dat ...]\\n\", argv[0]);\n\t\texit(-1);\n\t}\n\n\tfor (i = 1; i < argc; i++) {\n\t\thandles = realloc(handles, sizeof(*handles) * (nr_handles + 1));\n\t\tif (!handles)\n\t\t\texit(-1);\n\t\thandles[nr_handles] = tracecmd_open(argv[i], 0);\n\t\tif (!handles[nr_handles])\n\t\t\texit(-1);\n\n\t\tret = tracecmd_get_tsc2nsec(handles[nr_handles], &multi, &shift, &offset);\n\t\tif (!ret)\n\t\t\tprintf(\" %s has tsc2nsec calculations of mult:%d shift:%d offset:%lld\\n\",\n\t\t\t\targv[i], multi, shift, offset);\n\t\ttracecmd_set_private(handles[nr_handles], argv[i]);\n\t\tts = tracecmd_get_first_ts(handles[nr_handles]);\n\t\tif (!first_ts || ts < first_ts)\n\t\t\tfirst_ts = ts;\n\t\tnr_handles++;\n\t}\n\n\t/* Set the time stamp to start at the first record found */\n\tfor (i = 0; i < nr_handles; i++)\n\t\ttracecmd_add_ts_offset(handles[i], -first_ts);\n\n\ttracecmd_iterate_events_multi(handles, nr_handles, print_events, NULL);\n\n\tfor (i = 0; i < nr_handles; i++)\n\t\ttracecmd_close(handles[i]);\n\tfree(handles);\n}\n--\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n_libtracefs(3)_,\n_libtraceevent(3)_,\n_trace-cmd(1)_\n_trace-cmd.dat(5)_\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2020 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/libtracecmd.txt",
    "content": "libtracecmd(3)\n=============\n\nNAME\n----\nlibtracecmd - trace-cmd library APIs\n\nSYNOPSIS\n--------\n[verse]\n--\n*#include <trace-cmd.h>*\n\nOpen and close trace file:\n\tstruct tracecmd_input pass:[*]*tracecmd_open*(const char pass:[*]_file_, int _flags_);\n\tstruct tracecmd_input pass:[*]*tracecmd_open_fd*(int _fd_, int _flags_);\n\tstruct tracecmd_input pass:[*]*tracecmd_open_head*(const char pass:[*]_file_, int _flags_);\n\tvoid *tracecmd_close*(struct tracecmd_input pass:[*]_handle_);\n\tvoid *tracecmd_set_private*(struct tracecmd_input pass:[*]_handle_, void pass:[*]_data_);\n\tvoid pass:[*]*tracecmd_get_private*(struct tracecmd_input pass:[*]_handle_);\n\nRead tracing records from a trace file:\n\tint *tracecmd_init_data*(struct tracecmd_input pass:[*]_handle_);\n\tstruct tep_record pass:[*]*tracecmd_read_cpu_first*(struct tracecmd_input pass:[*]_handle_, int _cpu_);\n\tstruct tep_record pass:[*]*tracecmd_read_data*(struct tracecmd_input pass:[*]_handle_, int _cpu_);\n\tstruct tep_record pass:[*]*tracecmd_read_at*(struct tracecmd_input pass:[*]_handle_, unsigned long long _offset_, int pass:[*]_cpu_);\n\tvoid *tracecmd_free_record*(struct tep_record pass:[*]_record_);\n\tstruct tep_handle pass:[*]*tracecmd_get_tep*(struct tracecmd_input pass:[*]_handle_);\n\nIterating over events in a trace file:\n\tint *tracecmd_iterate_events*(struct tracecmd_input pass:[*]_handle_,\n\t\t\t\t    cpu_set_t pass:[*]_cpus_, int _cpu_size_,\n\t\t\t\t    int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t\t    struct tep_record pass:[*],\n\t\t\t\t\t\t    int, void pass:[*]),\n\t\t\t\t    void pass:[*]_callback_data_);\n\tint *tracecmd_iterate_events_multi*(struct tracecmd_input pass:[**]_handles_,\n\t\t\t\t\t  int _nr_handles_,\n\t\t\t\t\t  int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t\t\t\t   struct tep_record pass:[*],\n\t\t\t\t\t\t\t   int, void pass:[*]),\n\t\t\t\t\t  void pass:[*]_callback_data_);\n\tint *tracecmd_iterate_events_reverse*(struct tracecmd_input pass:[*]_handle_,\n\t\t\t\t    cpu_set_t pass:[*]_cpus_, int _cpu_size_,\n\t\t\t\t    int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t\t    struct tep_record pass:[*],\n\t\t\t\t\t\t    int, void pass:[*]),\n\t\t\t\t    void pass:[*]_callback_data_, bool _cont_);\n\tint *tracecmd_follow_event*(struct tracecmd_input pass:[*]_handle_,\n\t\t\t\t  const char pass:[*]_system_, const char pass:[*]_event_name_,\n\t\t\t\t  int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t\t  struct tep_event pass:[*],\n\t\t\t\t\t\t  struct tep_record pass:[*],\n\t\t\t\t\t\t  int, void pass:[*]),\n\t\t\t\t  void pass:[*]_callback_data_);\n\tint *tracecmd_follow_missed_events*(struct tracecmd_input pass:[*]_handle_,\n\t\t\t\t\t   int (pass:[*]_callback_)(struct tracecmd_input pass:[*],\n\t\t\t\t\t\t\t   struct tep_event pass:[*],\n\t\t\t\t\t\t\t   struct tep_record pass:[*],\n\t\t\t\t\t\t\t   int, void pass:[*]),\n\t\t\t\t\t   void pass:[*]_callback_data_);\n\tstruct tracecmd_filter pass:[*]*tracecmd_filter_add*(struct tracecmd_input *_handle_,\n\t\t\t\t\t\t    const char pass:[*]_filter_str_, bool _neg_);\n\tint *tracecmd_iterate_reset*(struct tracecmd_input pass:[*]_handle_);\n\nRead tracing instances from a trace file:\n\tint *tracecmd_buffer_instances*(struct tracecmd_input pass:[*]_handle_);\n\tconst char pass:[*]*tracecmd_buffer_instance_name*(struct tracecmd_input pass:[*]_handle_, int _indx_);\n\tstruct tracecmd_input pass:[*]*tracecmd_buffer_instance_handle*(struct tracecmd_input pass:[*]_handle_, int _indx_);\n\nHandle time stamps from a trace file:\n\tunsigned long long *tracecmd_get_first_ts*(struct tracecmd_input pass:[*]_handle_);\n\tvoid *tracecmd_add_ts_offset*(struct tracecmd_input pass:[*]_handle_, long long _offset_);\n\tint *tracecmd_get_tsc2nsec*(struct tracecmd_input pass:[*]_handle_, int pass:[*]_mult_, int pass[*]_shift_, unsigned long long pass:[*]_offset_);\n\nGet traceing peer information from a trace file:\n\tunsigned long long *tracecmd_get_traceid*(struct tracecmd_input pass:[*]_handle_);\n\tint *tracecmd_get_guest_cpumap*(struct tracecmd_input pass:[*]_handle_, unsigned long long _trace_id_, const char pass:[*]pass:[*]_name_, int pass:[*]_vcpu_count_, const int pass:[*]pass:[*]_cpu_pid_);\n\nMapping host and guest trace files:\n\tint *tracecmd_map_vcpus*(struct tracecmd_input pass:[**]handles, int nr_handles);\n\tstruct tracecmd_cpu_map pass:[*]*tracecmd_get_cpu_map*(struct tracecmd_input pass:[*]handle, int cpu);\n\tstruct tracecmd_cpu_map pass:[*]*tracecmd_map_find_by_host_pid*(struct tracecmd_input pass:[*]handle,\n\t\t\t\t\t\t      int host_pid);\n\tint *tracecmd_map_get_host_pid*(struct tracecmd_cpu_map pass:[*]map);\n\tstruct tracecmd_input pass:[*]*tracecmd_map_get_guest*(struct tracecmd_cpu_map pass:[*]map);\n\tvoid *tracecmd_map_set_private*(struct tracecmd_cpu_map pass:[*]map, void pass:[*]priv);\n\tvoid pass:[*]*tracecmd_map_get_private*(struct tracecmd_cpu_map pass:[*]map);\n\nControl library logs:\n\tint *tracecmd_set_loglevel*(enum tep_loglevel _level_);\n--\n\nDESCRIPTION\n-----------\nThe libtracecmd(3) library provides APIs to read, parse and write\n_trace-cmd.dat(5)_ files, recorded with _trace-cmd(1)_ application and containing\ntracing information from ftrace, the official Linux kernel tracer.\n\nFILES\n-----\n[verse]\n--\n*trace-cmd.h*\n\tHeader file to include in order to have access to the library APIs.\n*-ltracecmd*\n\tLinker switch to add when building a program that uses the library.\n--\n\nSEE ALSO\n--------\n*libtraceevent(3)*\n*libtracefs(3)*\n*trace-cmd(1)*\n*trace-cmd.dat(5)*\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\nlibtracecmd is Free Software licensed under the GNU LGPL 2.1\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2020 VMware, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/libtracecmd/meson.build",
    "content": "# SPDX-License-Identifier: LGPL-2.1\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\n# input text file: man page section\n\nsources = {\n    'libtracecmd-files.txt': '3',\n    'libtracecmd-instances.txt': '3',\n    'libtracecmd-iterate.txt': '3',\n    'libtracecmd-log.txt': '3',\n    'libtracecmd-maps.txt': '3',\n    'libtracecmd-peer.txt': '3',\n    'libtracecmd-record.txt': '3',\n    'libtracecmd-timestamp.txt': '3',\n    'libtracecmd.txt': '3',\n}\n\nconfdir = meson.current_source_dir() + '/../'\ntop_source_dir = meson.current_source_dir() + '/../../'\n\n#\n# For asciidoc ...\n#   -7.1.2,     no extra settings are needed.\n#    8.0-,      set ASCIIDOC8.\n#\n\n#\n# For docbook-xsl ...\n#   -1.68.1,         set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)\n#    1.69.0,         no extra settings are needed?\n#    1.69.1-1.71.0,  set DOCBOOK_SUPPRESS_SP?\n#    1.71.1,         no extra settings are needed?\n#    1.72.0,         set DOCBOOK_XSL_172.\n#    1.73.0-,        set ASCIIDOC_NO_ROFF\n#\n\n#\n# If you had been using DOCBOOK_XSL_172 in an attempt to get rid\n# of 'the \".ft C\" problem' in your generated manpages, and you\n# instead ended up with weird characters around callouts, try\n# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).\n#\n\nif get_option('asciidoctor')\n    asciidoc = find_program('asciidoctor')\n    asciidoc_extra  = ['-a', 'compat-mode']\n    asciidoc_extra += ['-I.']\n    asciidoc_extra += ['-r', 'asciidoctor-extensions']\n    asciidoc_extra += ['-a', 'mansource=libtraceevent']\n    asciidoc_extra += ['-a', 'manmanual=\"libtraceevent Manual\"']\n    asciidoc_html = 'xhtml5'\nelse\n    asciidoc = find_program('asciidoc')\n    asciidoc_extra  = ['--unsafe']\n    asciidoc_extra += ['-f', confdir + 'asciidoc.conf']\n    asciidoc_html = 'xhtml11'\n\n    r = run_command(asciidoc, '--version', check: true)\n    v = r.stdout().strip()\n    if v.version_compare('>=8.0')\n        asciidoc_extra += ['-a', 'asciidoc7compatible']\n    endif\nendif\n\nmanpage_xsl = confdir + 'manpage-normal.xsl'\n\nif get_option('docbook-xls-172')\n    asciidoc_extra += ['-a', 'libtraceevent-asciidoc-no-roff']\n    manpage_xsl = confdir + 'manpage-1.72.xsl'\nelif get_option('asciidoc-no-roff')\n    # docbook-xsl after 1.72 needs the regular XSL, but will not\n    # pass-thru raw roff codes from asciidoc.conf, so turn them off.\n    asciidoc_extra += ['-a', 'libtraceevent-asciidoc-no-roff']\nendif\n\nxmlto = find_program('xmlto')\nxmlto_extra = []\n\nif get_option('man-bold-literal')\n    xmlto_extra += ['-m ', confdir + 'manpage-bold-literal.xsl']\nendif\n\nif get_option('docbook-suppress-sp')\n    xmlto_extra += ['-m ',  confdir + 'manpage-suppress-sp.xsl']\nendif\n\ncheck_doc = custom_target(\n    'check-doc',\n    output: 'dummy',\n    command : [\n        top_source_dir + 'check-manpages.sh',\n        meson.current_source_dir()])\n\ngen = generator(\n    asciidoc,\n    output: '@BASENAME@.xml',\n    arguments: [\n        '-b', 'docbook',\n        '-d', 'manpage',\n        '-a', 'libtraceevent_version=' + meson.project_version(),\n        '-o', '@OUTPUT@']\n        + asciidoc_extra\n        +  ['@INPUT@'])\n\nman = []\nhtml = []\nforeach txt, section : sources\n    # build man page(s)\n    xml = gen.process(txt)\n    man += custom_target(\n        txt.underscorify() + '_man',\n        input: xml,\n        output: '@BASENAME@.' + section,\n        depends: check_doc,\n        command: [\n            xmlto,\n            '-m', manpage_xsl,\n            'man',\n            '-o', '@OUTPUT@']\n            + xmlto_extra\n            + ['@INPUT@'])\n\n    # build html pages\n    html += custom_target(\n        txt.underscorify() + '_html',\n        input: txt,\n        output: '@BASENAME@.html',\n        depends: check_doc,\n        command: [\n            asciidoc,\n            '-b', asciidoc_html,\n            '-d', 'manpage',\n            '-a', 'libtraceevent_version=' + meson.project_version(),\n            '-o', '@OUTPUT@']\n            + asciidoc_extra\n            + ['@INPUT@'])\nendforeach\n\n# Install path workaround because:\n#\n# - xmlto might generate more than one file and we would to tell meson\n#   about those output files. We could figure out which files are generated\n#   (see sed match in check-manpages.sh).\n#\n# - The man page generation puts all the generated files under sub dirs\n#   and it's not obvious how to tell Meson it should not do this without\n#   causing the install step to fail (confusion where the generated files\n#   are stored)\n#\n# - The documentation build is not part of the 'build' target. The user\n#   has explicitly to trigger the doc build. Hence the documentation is\n#   not added to the 'install' target.\n#\n# Thus just use a plain old shell script to move the generated files to the\n# right location.\n\nconf = configuration_data()\nconf.set('SRCDIR', meson.current_build_dir())\nconf.set('MANDIR', mandir)\nconf.set('HTMLDIR', htmldir)\nconfigure_file(\n    input: 'install-docs.sh.in',\n    output: 'install-docs.sh',\n    configuration: conf)\n\nmeson.add_install_script(\n    join_paths(meson.current_build_dir(), 'install-docs.sh'))\n"
  },
  {
    "path": "Documentation/manpage-1.72.xsl",
    "content": "<!-- manpage-1.72.xsl:\n     special settings for manpages rendered from asciidoc+docbook\n     handles peculiarities in docbook-xsl 1.72.0 -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n\t\tversion=\"1.0\">\n\n<xsl:import href=\"manpage-base.xsl\"/>\n\n<!-- these are the special values for the roff control characters\n     needed for docbook-xsl 1.72.0 -->\n<xsl:param name=\"git.docbook.backslash\">&#x2593;</xsl:param>\n<xsl:param name=\"git.docbook.dot\"      >&#x2302;</xsl:param>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "Documentation/manpage-base.xsl",
    "content": "<!-- manpage-base.xsl:\n     special formatting for manpages rendered from asciidoc+docbook -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n\t\tversion=\"1.0\">\n\n<!-- these params silence some output from xmlto -->\n<xsl:param name=\"man.output.quietly\" select=\"1\"/>\n<xsl:param name=\"refentry.meta.get.quietly\" select=\"1\"/>\n\n<!-- convert asciidoc callouts to man page format;\n     git.docbook.backslash and git.docbook.dot params\n     must be supplied by another XSL file or other means -->\n<xsl:template match=\"co\">\n\t<xsl:value-of select=\"concat(\n\t\t\t      $git.docbook.backslash,'fB(',\n\t\t\t      substring-after(@id,'-'),')',\n\t\t\t      $git.docbook.backslash,'fR')\"/>\n</xsl:template>\n<xsl:template match=\"calloutlist\">\n\t<xsl:value-of select=\"$git.docbook.dot\"/>\n\t<xsl:text>sp&#10;</xsl:text>\n\t<xsl:apply-templates/>\n\t<xsl:text>&#10;</xsl:text>\n</xsl:template>\n<xsl:template match=\"callout\">\n\t<xsl:value-of select=\"concat(\n\t\t\t      $git.docbook.backslash,'fB',\n\t\t\t      substring-after(@arearefs,'-'),\n\t\t\t      '. ',$git.docbook.backslash,'fR')\"/>\n\t<xsl:apply-templates/>\n\t<xsl:value-of select=\"$git.docbook.dot\"/>\n\t<xsl:text>br&#10;</xsl:text>\n</xsl:template>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "Documentation/manpage-bold-literal.xsl",
    "content": "<!-- manpage-bold-literal.xsl:\n     special formatting for manpages rendered from asciidoc+docbook -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n\t\tversion=\"1.0\">\n\n<!-- render literal text as bold (instead of plain or monospace);\n     this makes literal text easier to distinguish in manpages\n     viewed on a tty -->\n<xsl:template match=\"literal\">\n\t<xsl:value-of select=\"$git.docbook.backslash\"/>\n\t<xsl:text>fB</xsl:text>\n\t<xsl:apply-templates/>\n\t<xsl:value-of select=\"$git.docbook.backslash\"/>\n\t<xsl:text>fR</xsl:text>\n</xsl:template>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "Documentation/manpage-normal.xsl",
    "content": "<!-- manpage-normal.xsl:\n     special settings for manpages rendered from asciidoc+docbook\n     handles anything we want to keep away from docbook-xsl 1.72.0 -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n\t\tversion=\"1.0\">\n\n<xsl:import href=\"manpage-base.xsl\"/>\n\n<!-- these are the normal values for the roff control characters -->\n<xsl:param name=\"git.docbook.backslash\">\\</xsl:param>\n<xsl:param name=\"git.docbook.dot\"\t>.</xsl:param>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "Documentation/manpage-suppress-sp.xsl",
    "content": "<!-- manpage-suppress-sp.xsl:\n     special settings for manpages rendered from asciidoc+docbook\n     handles erroneous, inline .sp in manpage output of some\n     versions of docbook-xsl -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n\t\tversion=\"1.0\">\n\n<!-- attempt to work around spurious .sp at the tail of the line\n     that some versions of docbook stylesheets seem to add -->\n<xsl:template match=\"simpara\">\n  <xsl:variable name=\"content\">\n    <xsl:apply-templates/>\n  </xsl:variable>\n  <xsl:value-of select=\"normalize-space($content)\"/>\n  <xsl:if test=\"not(ancestor::authorblurb) and\n                not(ancestor::personblurb)\">\n    <xsl:text>&#10;&#10;</xsl:text>\n  </xsl:if>\n</xsl:template>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "Documentation/trace-cmd/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\n# Include the utils\ninclude $(src)/scripts/utils.mk\n\n# This Makefile and manpage XSL files were taken from libtracefs\n# and modified for libtracecmd\n\nMAN1_TXT= \\\n\t$(wildcard trace-cmd*.1.txt)\n\nMAN5_TXT= \\\n\t$(wildcard trace-cmd*.5.txt)\n\nMAN_TXT = $(MAN1_TXT) $(MAN5_TXT)\n_MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))\n_MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))\n_DOC_MAN1=$(patsubst %.1.txt,%.1,$(MAN1_TXT))\n_DOC_MAN5=$(patsubst %.5.txt,%.5,$(MAN5_TXT))\n\nMAN_XML=$(addprefix $(OUTPUT),$(_MAN_XML))\nMAN_HTML=$(addprefix $(OUTPUT),$(_MAN_HTML))\nDOC_MAN1=$(addprefix $(OUTPUT),$(_DOC_MAN1))\nDOC_MAN5=$(addprefix $(OUTPUT),$(_DOC_MAN5))\n\n# Make the path relative to DESTDIR, not prefix\nifndef DESTDIR\nprefix?=$(HOME)\nendif\nbindir?=$(prefix)/bin\nhtmldir?=$(prefix)/share/doc/trace-cmd\npdfdir?=$(prefix)/share/doc/trace-cmd\nmandir?=$(prefix)/share/man\nman1dir=$(mandir)/man1\nman5dir=$(mandir)/man5\n\nifdef USE_ASCIIDOCTOR\nASCIIDOC_EXTRA += -a mansource=\"libtracecmd\" -a manmanual=\"libtracecmd Manual\"\nendif\n\nall: check-man-tools html man\n\nman: man1 man5\nman1: $(DOC_MAN1)\nman5: $(DOC_MAN5)\n\nhtml: $(MAN_HTML)\n\n$(MAN_HTML) $(DOC_MAN1) $(DOC_MAN5): $(ASCIIDOC_CONF)\n\ninstall: check-man-tools install-man install-html\n\ncheck-man-tools:\nifdef missing_tools\n\t$(error \"You need to install $(missing_tools) for man pages\")\nendif\n\ninstall-%.1: $(OUTPUT)%.1\n\t$(Q)$(call do_install_docs,$<,$(man1dir),644);\n\ninstall-%.5: $(OUTPUT)%.5\n\t$(Q)$(call do_install_docs,$<,$(man5dir),644);\n\ndo-install-man: man $(addprefix install-,$(wildcard $(OUTPUT)*.1)) \\\n\t$(addprefix install-,$(wildcard $(OUTPUT)*.5))\n\ninstall-man: man\n\t$(Q)$(MAKE) -C . do-install-man\n\ninstall-%.txt: $(OUTPUT)%.html\n\t$(Q)$(call do_install_docs,$<,$(htmldir),644);\n\ndo-install-html: html $(addprefix install-,$(wildcard *.txt))\n\ninstall-html: html do-install-html\n\nuninstall: uninstall-man uninstall-html\n\nuninstall-man:\n\t$(Q)$(RM) $(addprefix $(DESTDIR)$(man1dir)/,$(DOC_MAN1))\n\t$(Q)$(RM) $(addprefix $(DESTDIR)$(man5dir)/,$(DOC_MAN5))\n\nuninstall-html:\n\t$(Q)$(RM) $(addprefix $(DESTDIR)$(htmldir)/,$(MAN_HTML))\n\nifdef missing_tools\n  DO_INSTALL_MAN = $(warning Please install $(missing_tools) to have the man pages installed)\nelse\n  DO_INSTALL_MAN = do-install-man\nendif\n\nCLEAN_FILES =\t\t\t\t\t\\\n\t$(MAN_XML) $(addsuffix +,$(MAN_XML))\t\\\n\t$(MAN_HTML) $(addsuffix +,$(MAN_HTML))\t\\\n\t$(DOC_MAN1) $(DOC_MAN5) *.1 *.5\n\nclean:\n\t$(Q) $(RM) $(CLEAN_FILES)\n\nifdef USE_ASCIIDOCTOR\n$(OUTPUT)%.1 : $(OUTPUT)%.1.txt\n\t$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \\\n\t$(ASCIIDOC) -b manpage -d manpage \\\n\t\t$(ASCIIDOC_EXTRA) -atracecmd_version=$(TRACECMD_VERSION) -o $@+ $< && \\\n\tmv $@+ $@\n\n$(OUTPUT)%.5 : $(OUTPUT)%.5.txt\n\t$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \\\n\t$(ASCIIDOC) -b manpage -d manpage \\\n\t\t$(ASCIIDOC_EXTRA) -atracecmd_version=$(TRACECMD_VERSION) -o $@+ $< && \\\n\tmv $@+ $@\nendif\n\n$(OUTPUT)%.1 : $(OUTPUT)%.1.xml\n\t$(QUIET_XMLTO)$(RM) $@ && \\\n\t$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<; \\\n\n$(OUTPUT)%.5 : $(OUTPUT)%.5.xml\n\t$(QUIET_XMLTO)$(RM) $@ && \\\n\t$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<; \\\n\n$(OUTPUT)%.xml : %.txt\n\t$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \\\n\t$(ASCIIDOC) -b docbook -d manpage \\\n\t\t$(ASCIIDOC_EXTRA) -atracecmd_version=$(TRACECMD_VERSION) -o $@+ $< && \\\n\tmv $@+ $@\n\n$(MAN_HTML): $(OUTPUT)%.html : %.txt\n\t$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \\\n\t$(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \\\n\t\t$(ASCIIDOC_EXTRA) -atracecmd_version=$(TRACECMD_VERSION) -o $@+ $< && \\\n\tmv $@+ $@\n"
  },
  {
    "path": "Documentation/trace-cmd/install-docs.sh.in",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: LGPL-2.1\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nfor section in 1 3 5; do\n    while IFS= read -r -d '' man; do\n        [ ! -d \"${DESTDIR}@MANDIR@/man${section}\" ] && install -d \"${DESTDIR}@MANDIR@/man${section}\"\n\n        echo Installing \"${man}\" to \"${DESTDIR}@MANDIR@/man${section}\"\n        install -m 0644 \"${man}\" \"${DESTDIR}@MANDIR@/man${section}/\"\n    done< <(find \"@SRCDIR@\" -name \"*\\.${section}\" -type f -print0)\ndone\n\nwhile IFS= read -r -d '' html; do\n    [ ! -d \"${DESTDIR}@HTMLDIR@\"  ] && install -d \"${DESTDIR}@HTMLDIR@\"\n\n    echo Installing \"${html}\" to \"${DESTDIR}@HTMLDIR@\"\n    install -m 0644 \"${html}\" \"${DESTDIR}@HTMLDIR@\"\ndone< <(find \"@SRCDIR@\" -name \"*\\.html\" -type f -print0)\n"
  },
  {
    "path": "Documentation/trace-cmd/meson.build",
    "content": "# SPDX-License-Identifier: LGPL-2.1\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\n# input text file: man page section\n\nsources = {\n    'trace-cmd.1.txt': '1',\n    'trace-cmd-agent.1.txt': '1',\n    'trace-cmd-check-events.1.txt': '1',\n    'trace-cmd-clear.1.txt': '1',\n    'trace-cmd-convert.1.txt': '1',\n    'trace-cmd.dat.v6.5.txt': '5',\n    'trace-cmd.dat.v7.5.txt': '5',\n    'trace-cmd-dump.1.txt': '1',\n    'trace-cmd-extract.1.txt': '1',\n    'trace-cmd-hist.1.txt': '1',\n    'trace-cmd-list.1.txt': '1',\n    'trace-cmd-listen.1.txt': '1',\n    'trace-cmd-mem.1.txt': '1',\n    'trace-cmd-options.1.txt': '1',\n    'trace-cmd-profile.1.txt': '1',\n    'trace-cmd-record.1.txt': '1',\n    'trace-cmd-report.1.txt': '1',\n    'trace-cmd-reset.1.txt': '1',\n    'trace-cmd-restore.1.txt': '1',\n    'trace-cmd-set.1.txt': '1',\n    'trace-cmd-show.1.txt': '1',\n    'trace-cmd-snapshot.1.txt': '1',\n    'trace-cmd-split.1.txt': '1',\n    'trace-cmd-stack.1.txt': '1',\n    'trace-cmd-start.1.txt': '1',\n    'trace-cmd-stat.1.txt': '1',\n    'trace-cmd-stop.1.txt': '1',\n    'trace-cmd-stream.1.txt': '1',\n}\n\nconfdir = meson.current_source_dir() + '/../'\ntop_source_dir = meson.current_source_dir() + '/../../'\n\n#\n# For asciidoc ...\n#   -7.1.2,     no extra settings are needed.\n#    8.0-,      set ASCIIDOC8.\n#\n\n#\n# For docbook-xsl ...\n#   -1.68.1,         set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)\n#    1.69.0,         no extra settings are needed?\n#    1.69.1-1.71.0,  set DOCBOOK_SUPPRESS_SP?\n#    1.71.1,         no extra settings are needed?\n#    1.72.0,         set DOCBOOK_XSL_172.\n#    1.73.0-,        set ASCIIDOC_NO_ROFF\n#\n\n#\n# If you had been using DOCBOOK_XSL_172 in an attempt to get rid\n# of 'the \".ft C\" problem' in your generated manpages, and you\n# instead ended up with weird characters around callouts, try\n# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).\n#\n\nif get_option('asciidoctor')\n    asciidoc = find_program('asciidoctor')\n    asciidoc_extra  = ['-a', 'compat-mode']\n    asciidoc_extra += ['-I.']\n    asciidoc_extra += ['-r', 'asciidoctor-extensions']\n    asciidoc_extra += ['-a', 'mansource=libtraceevent']\n    asciidoc_extra += ['-a', 'manmanual=\"libtraceevent Manual\"']\n    asciidoc_html = 'xhtml5'\nelse\n    asciidoc = find_program('asciidoc')\n    asciidoc_extra  = ['--unsafe']\n    asciidoc_extra += ['-f', confdir + 'asciidoc.conf']\n    asciidoc_html = 'xhtml11'\n\n    r = run_command(asciidoc, '--version', check: true)\n    v = r.stdout().strip()\n    if v.version_compare('>=8.0')\n        asciidoc_extra += ['-a', 'asciidoc7compatible']\n    endif\nendif\n\nmanpage_xsl = confdir + 'manpage-normal.xsl'\n\nif get_option('docbook-xls-172')\n    asciidoc_extra += ['-a', 'libtraceevent-asciidoc-no-roff']\n    manpage_xsl = confdir + 'manpage-1.72.xsl'\nelif get_option('asciidoc-no-roff')\n    # docbook-xsl after 1.72 needs the regular XSL, but will not\n    # pass-thru raw roff codes from asciidoc.conf, so turn them off.\n    asciidoc_extra += ['-a', 'libtraceevent-asciidoc-no-roff']\nendif\n\nxmlto = find_program('xmlto')\nxmlto_extra = []\n\nif get_option('man-bold-literal')\n    xmlto_extra += ['-m ', confdir + 'manpage-bold-literal.xsl']\nendif\n\nif get_option('docbook-suppress-sp')\n    xmlto_extra += ['-m ',  confdir + 'manpage-suppress-sp.xsl']\nendif\n\ngen = generator(\n    asciidoc,\n    output: '@BASENAME@.xml',\n    arguments: [\n        '-b', 'docbook',\n        '-d', 'manpage',\n        '-a', 'libtraceevent_version=' + meson.project_version(),\n        '-o', '@OUTPUT@']\n        + asciidoc_extra\n        +  ['@INPUT@'])\n\nman = []\nhtml = []\nforeach txt, section : sources\n    # build man page(s)\n    xml = gen.process(txt)\n    man += custom_target(\n        txt.underscorify() + '_man',\n        input: xml,\n        output: '@BASENAME@.' + section,\n        command: [\n            xmlto,\n            '-m', manpage_xsl,\n            'man',\n            '-o', '@OUTPUT@']\n            + xmlto_extra\n            + ['@INPUT@'])\n\n    # build html pages\n    html += custom_target(\n        txt.underscorify() + '_html',\n        input: txt,\n        output: '@BASENAME@.html',\n        command: [\n            asciidoc,\n            '-b', asciidoc_html,\n            '-d', 'manpage',\n            '-a', 'libtraceevent_version=' + meson.project_version(),\n            '-o', '@OUTPUT@']\n            + asciidoc_extra\n            + ['@INPUT@'])\nendforeach\n\n# Install path workaround because:\n#\n# - xmlto might generate more than one file and we would to tell meson\n#   about those output files. We could figure out which files are generated\n#   (see sed match in check-manpages.sh).\n#\n# - The man page generation puts all the generated files under sub dirs\n#   and it's not obvious how to tell Meson it should not do this without\n#   causing the install step to fail (confusion where the generated files\n#   are stored)\n#\n# - The documentation build is not part of the 'build' target. The user\n#   has explicitly to trigger the doc build. Hence the documentation is\n#   not added to the 'install' target.\n#\n# Thus just use a plain old shell script to move the generated files to the\n# right location.\n\nconf = configuration_data()\nconf.set('SRCDIR', meson.current_build_dir())\nconf.set('MANDIR', mandir)\nconf.set('HTMLDIR', htmldir)\nconfigure_file(\n    input: 'install-docs.sh.in',\n    output: 'install-docs.sh',\n    configuration: conf)\n\nmeson.add_install_script(\n    join_paths(meson.current_build_dir(), 'install-docs.sh'))\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-agent.1.txt",
    "content": "TRACE-CMD-AGENT(1)\n==================\n\nNAME\n----\ntrace-cmd-agent - Run as an agent on a machine (to be controlled by another machine)\n\nSYNOPSIS\n--------\n*trace-cmd agent* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) agent listens over a vsocket (for virtual machines) or a TCP port\nfor connections to control the tracing of the machine. The agent will then start\ntracing on the local machine and pass the data to the controlling connection.\n\nOPTIONS\n-------\n*-N* 'client'::\n    Listen over TCP instead of a vsocket. Must pass in a client host name or IP address\n    to allow connection to. It will only connect to the specified client. Note, any process\n    on that client can control the agent.\n\n    *This is a very insecure setting. Only use on a trusted network*\n    *Only use if the client machine is totally trusted*\n\n*-p* 'port'::\n    This option will specify the port to listen to.\n\n*-D*::\n    This options causes trace-cmd agent to go into a daemon mode and run in\n    the background.\n\n*-P* 'cid'::\n    Allow an agent to also act as a proxy server, where it can be run on a host\n    and connect with a guest. 'cid' is the context ID (see *vsock*(7)) of the\n    client (e.g., guest VM) it will allow to connect.\n\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"critical\", \"error\", \"warning\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd listen --verbose=warning\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-split(1), trace-cmd-list(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-attach.1.txt",
    "content": "TRACE-CMD-ATTACH(1)\n===================\n\nNAME\n----\ntrace-cmd-attach - attach a guest trace.dat file to a host trace.dat file\n\nSYNOPSIS\n--------\n*trace-cmd attach* ['OPTIONS'] host-trace-file guest-trace-file guest-pid [guest-pid ...]\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) attach is used to take a trace.dat file created on a guest and\nattach it to a trace.dat file that was created on the host. In most cases,\ntrace-cmd-agent(1) can be used to automate this, but if for some reason, the\nagent isn't appropriate, it may be required to start trace-cmd recording on\nthe guest with trace-cmd-record(1). If the host recording is activated at the\nsame time, one can use trace-cmd attach(1) to connect the guest and host files\nas if they were created by the trace-cmd agent.\n\n*host-trace-file*::\n    The trace.dat file created by the host. Must have kvm_exit and kvm_entry\n    events, and use the \"tsc2nsec\" clock.\n\n*guest-trace-file*::\n    The trace.dat file created by the guest. Must use the \"x86-tsc\" clock.\n    For now, this is only supported on x86, it may support other achitectures\n    later.\n\n*guest-pid*::\n    The process ID of the host thread that represents the guests threads.\n    Each process ID that represents all of the guest vCPUs should be listed.\n    Note, you can add more than just the threads that represent the guest\n    vCPUs, as the tool will search the *host-trace-file* for kvm_exit and\n    kvm_entry events to match these PIDs with the vCPUs that they represent.\n\nOPTIONS\n-------\n*-c* 'cpus'::\n    Specify a the number of CPUS the guest has.\n\n*-s* 'timeshift'::\n    A comma separated list of the format _offset_,_scale_,_frac_,_timestamp_\n    These values map to what are given in /sys/kernel/kvm/<pid>/vcpu/*\n\n    *offset*: Is the offset of the guest. \"tsc-offest\" in the directory. Note\n              that the value listed here is the negative of what is listed in\n              the directory.\n\n    *scale*:  The scaling factor. \"tsc-scaling-ratio\"\n\n    *frac*:   The fraction bits. \"tsc-scaling-ratio-frac-bits\"\n\n    *timestamp*: The timestamp to start using the above. In some cases, the\n              values may change over time. By adding a timestamp, it will\n              take effect after the timestamp has been hit. Normally\n              this would be zero.\n\n   Currently, only one timeshift is given per CPU. One *-s* option should be\n   given for each CPU. If there are less options than CPUs, then the last option\n   given will be used for the rest of the CPUs. If only one option is given, then\n   the values for that option will be used for all CPUs.\n\nEXAMPLES\n--------\n\nEnable all events for tracing:\n\n[source,shell]\n----\n  $ # find the process for a given guest\n  $ ps aux |grep qemu\n libvirt+   63170  5.6  1.6 13994848 4257540 ?    Sl   May02 2884:49 /usr/bin/qemu-system-x86_64...\n\n  $ # Use 63170 to find all the PIDs for the give guest\n  $ ls /proc/63170/task\n 1541591  63170  63198  63209  63211  63213  63214  63215  63216  63217  63218  63219  63234\n\n  $ # Find the tsc offset\n  $ su\n  # cat /sys/kernel/debug/kvm/63170-15/vcpu0/tsc-offset\n -27950965013436847\n\n  # trace-cmd record -C tsc2nsec -e kvm -e sched -e irq -e timer\n\n# on guest:\n\n  # trace-cmd record -C x86-tsc -e sched -e irq -e timer sleep 10\n\n# back on host, hit Ctrl^C to stop tracing after the guest is done\n\n  # # Make the trace.dat user owned by your user account.\n  # chown user.user trace.dat\n  # exit\n\n  $ scp root@guest:trace.dat trace-guest.dat\n\n  $ # now attach the two files (guest has 8 CPUs)\n  $ trace-cmd attach -c 8 -s 27950965013436847 trace.dat trace-guest.dat 1541591  63170  63198  63209  63211  63213  63214  63215  63216  63217  63218  63219  63234\n\n  $ trace-cmd report -i trace.dat -i trace-guest.dat\n  $ # now you should see the guest trace interleaved within the host trace.\n----\n\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1), trace-cmd-profile(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt (Google) <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-check-events.1.txt",
    "content": "TRACE-CMD-CHECK_EVENTS(1)\n=========================\n\nNAME\n----\ntrace-cmd-check-events - parse the event formats on local system\n\nSYNOPSIS\n--------\n*trace-cmd check-events* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) check-events parses format strings for all the events on the\nlocal system. It returns whether all the format strings can be parsed\ncorrectly. It will load plugins unless specified otherwise.\n\nThis is useful to check for any trace event format strings which may contain\nsome internal kernel function references which cannot be decoded outside of\nthe kernel. This may mean that either the unparsed format strings of the trace\nevents need to be changed or that a plugin needs to be created to parse them.\n\nOPTIONS\n-------\n*-N* - Don't load plugins\n\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"critical\", \"error\", \"warning\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd check-events --verbose=warning\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-stop(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1), trace-cmd-start(1)\n\nAUTHOR\n------\nWritten by Vaibhav Nagarnaik, <vnagarnaik@google.com>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2011 Google, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-clear.1.txt",
    "content": "TRACE-CMD-CLEAR(1)\n=================\n\nNAME\n----\ntrace-cmd-clear - clear the Ftrace buffer.\n\nSYNOPSIS\n--------\n*trace-cmd clear* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe *trace-cmd(1) clear* clears the content of the Ftrace ring buffer.\n\nOPTIONS\n-------\n*-B* 'buffer-name'::\n    If the kernel supports multiple buffers, this will clear only the given\n    buffer. It does not affect any other buffers. This may be used multiple\n    times to specify different buffers. The top level buffer will not be\n    clearded if this option is given.\n\n*-a*::\n    Clear all existing buffers, including the top level one.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\n[verse]\n--\n*Steven Rostedt* <rostedt@goodmis.org>, author of *trace-cmd*.\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.\n--\nREPORTING BUGS\n--------------\nReport bugs to  <linux-trace-devel@vger.kernel.org>\n\nLICENSE\n-------\ntrace-cmd is Free Software licensed under the terms of the\nGNU Public License (GPL).\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-convert.1.txt",
    "content": "TRACE-CMD-CONVERT(1)\n===================\n\nNAME\n----\ntrace-cmd-convert - convert trace files\n\nSYNOPSIS\n--------\n*trace-cmd convert* ['OPTIONS'] ['output-file']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) convert command converts trace file. It reads the input file and copies the data\ninto an output file. The output file may be in different format, depending on the command line\narguments. The default output is in version 7 and compressed (if\ncompiled with compression support).\n\nOPTIONS\n-------\n*-i* 'input-file'::\n    By default, trace-cmd convert will read the file 'trace.dat'. But the *-i*\n    option open up the given 'input-file' instead.\n\n*-o* 'out-file'::\n    The name of the output file, this parameter is mandatory. Note, the output file may also be\n    specified as the last item on the command line.\n\n*--file-version*::\n    Desired version of the output file. Supported versions are 6 or 7.\n\n*--compression*::\n    Compression of the trace output file, one of these strings can be passed:\n\n    'any'  - auto select the best available compression algorithm\n\n    'none' - do not compress the trace file\n\n    'name' - the name of the desired compression algorithms. Available algorithms can be listed with\n    trace-cmd list -c\n\n*--help*::\n    Print usage information.\n\nEXAMPLES\n--------\n\n# trace-cmd convert --compression any trace_compress.dat\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd.dat(1)\n\nAUTHOR\n------\n*Steven Rostedt* <rostedt@goodmis.org>, author of *trace-cmd*.\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2021 VMware. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-dump.1.txt",
    "content": "TRACE-CMD-DUMP(1)\n===================\n\nNAME\n----\ntrace-cmd-dump - show a meta data from a trace file, created by trace-cmd record\n\nSYNOPSIS\n--------\n*trace-cmd dump* ['OPTIONS'] ['input-file']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) dump command will display the meta data from a trace file\ncreated by trace-cmd record.\n\nOPTIONS\n-------\n*-i* 'input-file'::\n    By default, trace-cmd dump will read the file 'trace.dat'. But the *-i*\n    option open up the given 'input-file' instead. Note, the input file may\n    also be specified as the last item on the command line.\n*-v*, *--validate*::\n    Check if the input file is a valid trace file, created by trace-cmd.\n*--summary*::\n    Print a meta data summary - initial format and a short description of each\n    file section. This is the default action, if no arguments are specified.\n*--head-page*::\n    Print the header page information, stored in the file.\n*--head-event*::\n    Print the event header information, stored in the file.\n*--ftrace-events*::\n    Print formats of ftrace specific events.\n*--systems*::\n    Print information of event systems, stored in the file - name and number of\n    events for each system.\n*--events*::\n    Print formats of all events, stored in the file.\n*--kallsyms*::\n    Print information of the mapping of function addresses to the function names.\n*--printk*::\n    Print trace_printk() format strings, stored in the file.\n*--cmd-lines*::\n    Print mapping a PID to a process name.\n*--options*::\n    Print all options, stored in the file.\n*--flyrecord*::\n    Print the offset and the size of tracing data per each CPU.\n*--clock*::\n    Print the trace clock, used for timestamp of the tracing events, stored in the file.\n*--all*::\n    Print all meta data from the file.\n*--help*::\n    Print usage information.\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"critical\", \"error\", \"warning\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd report --verbose=warning\n\nEXAMPLES\n--------\n\n[source,shell]\n----\n# trace-cmd dump --summary -i trace.dat\n\n Tracing meta data in file trace.dat:\n\t[Initial format]\n\t\t6\t[Version]\n\t\t0\t[Little endian]\n\t\t8\t[Bytes in a long]\n\t\t4096\t[Page size, bytes]\n\t[Header info, 205 bytes]\n\t[Header event, 205 bytes]\n\t[Ftrace format, 15 events]\n\t[Events format, 2 systems]\n\t[Kallsyms, 7144493 bytes]\n\t[Trace printk, 2131 bytes]\n\t[Saved command lines, 117 bytes]\n\t8 [CPUs with tracing data]\n\t[12 options]\n\t[Flyrecord tracing data]\n----\n\n[source,shell]\n----\n# trace-cmd dump --flyrecord -i trace.dat\n\t[Flyrecord tracing data]\n\t\t 7176192 0\t[offset, size of cpu 0]\n\t\t 7176192 0\t[offset, size of cpu 1]\n\t\t 7176192 0\t[offset, size of cpu 2]\n\t\t 7176192 4096\t[offset, size of cpu 3]\n\t\t 7180288 4096\t[offset, size of cpu 4]\n\t\t 7184384 0\t[offset, size of cpu 5]\n\t\t 7184384 0\t[offset, size of cpu 6]\n\t\t 7184384 0\t[offset, size of cpu 7]\n----\n\n[source,shell]\n----\n# trace-cmd dump --summary --systems -i trace.dat\n\n Tracing meta data in file trace.dat:\n\t[Initial format]\n\t\t6\t[Version]\n\t\t0\t[Little endian]\n\t\t8\t[Bytes in a long]\n\t\t4096\t[Page size, bytes]\n\t[Header info, 205 bytes]\n\t[Header event, 205 bytes]\n\t[Ftrace format, 15 events]\n\t[Events format, 3 systems]\n\t\tsched 23 [system, events]\n\t\tirq 5 [system, events]\n\t\tkvm 70 [system, events]\n\t[Kallsyms, 7144493 bytes]\n\t[Trace printk, 2131 bytes]\n\t[Saved command lines, 157 bytes]\n\t8 [CPUs with tracing data]\n\t[11 options]\n\t[Flyrecord tracing data]\n----\n\n[source,shell]\n----\n# trace-cmd dump --summary --systems -i trace.dat\nFile trace.dat is a valid trace-cmd file\n----\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd.dat(1)\n\nAUTHOR\n------\n*Steven Rostedt* <rostedt@goodmis.org>, author of *trace-cmd*.\n*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-extract.1.txt",
    "content": "TRACE-CMD-EXTRACT(1)\n====================\n\nNAME\n----\ntrace-cmd-extract - extract out the data from the Ftrace Linux tracer.\n\nSYNOPSIS\n--------\n*trace-cmd extract ['OPTIONS']*\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) extract is usually used after 'trace-cmd-start(1)' and\n'trace-cmd-stop(1)'. It can be used after the Ftrace tracer has been started\nmanually through the Ftrace pseudo file system.\n\nThe extract command creates a trace.dat file that can be used by\n'trace-cmd-report(1)' to read from. It reads the kernel internal ring buffer\nto produce the trace.dat file.\n\nOPTIONS\n-------\n*-p* 'plugin'::\n    Although *extract* does not start any traces, some of the plugins require\n    just reading the output in ASCII format. These are the latency tracers,\n    since the latency tracers have a separate internal buffer. The plugin\n    option is therefore only necessary for the 'wakeup', 'wakeup-rt',\n    'irqsoff', 'preemptoff' and 'preemptirqsoff' plugins.\n\n    With out this option, the extract command will extract from the internal\n    Ftrace buffers.\n\n*-O* 'option'::\n    If a latency tracer is being extracted, and the *-p* option is used, then\n    there are some Ftrace options that can change the format. This will update\n    those options before extracting.  To see the list of options see\n    'trace-cmd-list'. To enable an option, write its name, to disable the\n    option append the characters 'no' to it. For example: 'noprint-parent'\n    will disable the 'print-parent' option that prints the parent function in\n    printing a function event.\n\n*-o* 'outputfile'::\n    By default, the extract command will create a 'trace.dat' file. This\n    option will change where the file is written to.\n\n*-s*::\n    Extract from the snapshot buffer (if the kernel supports it).\n\n*--date*::\n    This is the same as the trace-cmd-record(1) --date option, but it\n    does cause the extract routine to disable all tracing. That is,\n    the end of the extract will perform something similar to trace-cmd-reset(1).\n\n*-B* 'buffer-name'::\n    If the kernel supports multiple buffers, this will extract the trace for\n    only the given buffer. It does not affect any other buffer. This may be\n    used multiple times to specify different buffers. When this option is\n    used, the top level instance will not be extracted unless *-t* is given.\n\n*-a*::\n    Extract all existing buffer instances. When this option is used, the\n    top level instance will not be extracted unless *-t* is given.\n\n*-t*::\n    Extracts the top level instance buffer. Without the *-B* or *-a* option\n    this is the same as the default. But if *-B* or *-a* is used, this is\n    required if the top level instance buffer should also be extracted.\n\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"critical\", \"error\", \"warning\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd extract --verbose=warning\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-hist.1.txt",
    "content": "TRACE-CMD-HIST(1)\n=================\n\nNAME\n----\ntrace-cmd-hist - show histogram of events in trace.dat file\n\nSYNOPSIS\n--------\n*trace-cmd hist* ['OPTIONS']['input-file']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) hist displays a histogram form from the trace.dat file.\nInstead of showing the events as they were ordered, it creates a histogram\nthat can be displayed per task or for all tasks where the most common\nevents appear first. It uses the function tracer and call stacks that it\nfinds to try to put together a call graph of the events.\n\nOPTIONS\n-------\n*-i* 'input-file'::\n    By default, trace-cmd hist will read the file 'trace.dat'. But the *-i*\n    option open up the given 'input-file' instead. Note, the input file may\n    also be specified as the last item on the command line.\n\n*-P*::\n    To compact all events and show the call graphs by ignoring tasks\n    and different PIDs, add the *-P* to do so. Instead of showing the\n    task name, it will group all chains together and show \"<all pids>\".\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-split(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-list.1.txt",
    "content": "TRACE-CMD-LIST(1)\n=================\n\nNAME\n----\ntrace-cmd-list - list available plugins, events or options for Ftrace.\n\nSYNOPSIS\n--------\n*trace-cmd list* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) list displays the available plugins, events or Ftrace options\nthat are configured on the current machine.  If no option is given, then it\nlists all plugins, event systems, events and Ftrace options to standard output.\n\nOPTIONS\n-------\n*-e* ['regex']::\n    This option will list the available events that are enabled on the\n    local system.\n\n    It takes an optional argument that uses 'regcomp(3)' expressions to seach.\n\n    trace-cmd list -e '^sys.*'\n\n*-F*::\n    Used with *-e* 'regex' to show the fields of the event.\n\n*--full*::\n    Used with *-F* which will show the \"print fmt\" of the event along with the fields.\n\n*-l*::\n    Used with *-e* 'regex' to show those events filters.\n\n*-R*::\n    Used with *-e* 'regex' to show those events triggers.\n\n*-s*::\n    This option will list the available event systems.\n\n*-t*::\n    This option will list the available tracers that are enabled on the\n    local system.\n\n*-p*::\n    Same as *-t* and only for legacy purposes.\n\n*-o*::\n    This option will list the available Ftrace options that are configured on\n    the local system.\n\n*-f* ['regex']::\n    This option will list the available filter functions. These are the list of\n    functions on the system that you can trace, or filter on.\n    It takes an optional argument that uses 'regcomp(3)' expressions to seach.\n\n    trace-cmd list -f '^sched.*'\n\n    If *--proto* is also added to the command line, the functions will also have\n    show their prototypes if BTF is available.\n\n*--proto*::\n    If BTF is available on the running kernel, it will also show the function\n    parameters with *-f*.\n\n*-P*::\n    List the plugin files that get loaded on trace-cmd report.\n\n*-O*::\n    List plugin options that can be used by trace-cmd report *-O* option.\n\n*-B*::\n    List defined buffer instances (sub buffers).\n\n*-C*::\n    List defined clocks that can be used with trace-cmd record -C.\n    The one in brackets ([]) is the active clock.\n\n*-c*::\n    List the available trace file compression algorithms.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-split(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-listen.1.txt",
    "content": "TRACE-CMD-LISTEN(1)\n===================\n\nNAME\n----\ntrace-cmd-listen - listen for incoming connection to record tracing.\n\nSYNOPSIS\n--------\n*trace-cmd listen* -p 'port' ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) listen sets up a port to listen to waiting for connections\nfrom other hosts that run 'trace-cmd-record(1)' with the *-N* option. When a\nconnection is made, and the remote host sends data, it will create a file\ncalled 'trace.HOST:PORT.dat'. Where HOST is the name of the remote host, and\nPORT is the port that the remote host used to connect with.\n\nOPTIONS\n-------\n*-p* 'port'::\n    This option will specify the port to listen to.\n\n*-D*::\n    This options causes trace-cmd listen to go into a daemon mode and run in\n    the background.\n\n*-V*::\n    Listen on a vsocket instead. This is useful for tracing between host and\n    guest VMs.\n\n*-d* 'dir'::\n    This option specifies a directory to write the data files into.\n\n*-o* 'filename'::\n    This option overrides the default 'trace' in the 'trace.HOST:PORT.dat' that\n    is created when a remote host connects.\n\n*-l* 'filename'::\n    This option writes the output messages to a log file instead of standard output.\n\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"critical\", \"error\", \"warning\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd listen --verbose=warning\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-split(1), trace-cmd-list(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-mem.1.txt",
    "content": "TRACE-CMD-MEM(1)\n================\n\nNAME\n----\ntrace-cmd-mem - show memory usage of certain kmem events\n\nSYNOPSIS\n--------\n*trace-cmd mem* ['OPTIONS']['input-file']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) mem requires a trace-cmd record that enabled the following\nevents:\n\n  kmalloc\n  kmalloc_node\n  kfree\n  kmem_cache_alloc\n  kmem_cache_alloc_node\n  kmem_cache_alloc_free\n\nIt then reads the amount requested and the ammount freed as well as the\nfunctions that called the allocation. It then reports the final amount\nof bytes requested and allocated, along with the total amount allocated\nand requested, as well as the max allocation and requested during the run.\nIt reports the amount of wasted bytes (allocated - requested) that was\nnot freed, as well as the max wasted amount during the run. The list is \nsorted by descending order of wasted bytes after the run.\n\n                Function                Waste   Alloc   req             TotAlloc     TotReq             MaxAlloc     MaxReq     MaxWaste\n                --------                -----   -----   ---             --------     ------             --------     ------     --------\n          rb_allocate_cpu_buffer        768     2304    1536                2304       1536                 2304       1536     768\n                 alloc_pipe_info        400     1152    752                 1152        752                 1152        752     400\n                  instance_mkdir        252     544     292                  544        292                  544        292     252\n                       __d_alloc        215     1086560 1086345          1087208    1086993              1086560    1086345     215\n                  get_empty_filp        72      2304    2232                4864       4712                 4864       4712     152\n                        mm_alloc        40      960     920                  960        920                  960        920     40\n                   prepare_creds        32      192     160                 1728       1440                 1728       1440     288\n            tracing_buffers_open        8       32      24                    32         24                   32         24     8\n                          do_brk        0       0       0                    368        368                  368        368     0\n        journal_add_journal_head        0       6048    6048                6048       6048                 6048       6048     0\n                   journal_start        0       0       0                   1224       1224                   48         48     0\n             __rb_allocate_pages        0       3289856 3289856          3289856    3289856              3289856    3289856     0\n                  anon_vma_alloc        0       0       0                    936        936                  864        864     0\n                                                                [...]\n\nOPTIONS\n-------\n*-i* 'input-file'::\n    By default, trace-cmd hist will read the file 'trace.dat'. But the *-i*\n    option open up the given 'input-file' instead. Note, the input file may\n    also be specified as the last item on the command line.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-hist(1),\ntrace-cmd-split(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2013 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-options.1.txt",
    "content": "TRACE-CMD-OPTIONS(1)\n====================\n\nNAME\n----\ntrace-cmd-options - list available options from trace-cmd plugins\n\nSYNOPSIS\n--------\n*trace-cmd options*\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) options command will examine all the trace-cmd plugins\nthat are used by *trace-cmd report(1)* and list them.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-start(1), trace-cmd-stop(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2011 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-profile.1.txt",
    "content": "TRACE-CMD-PROFILE(1)\n====================\n\nNAME\n----\ntrace-cmd-profile - profile tasks running live\n\nSYNOPSIS\n--------\n*trace-cmd profile ['OPTIONS']* ['command']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) profile will start tracing just like trace-cmd-record(1),\nwith the *--profile* option, except that it does not write to a file,\nbut instead, it will read the events as they happen and will update the accounting\nof the events. When the trace is finished, it will report the results just like\ntrace-cmd-report(1) would do with its *--profile* option. In other words,\nthe profile command does the work of trace-cmd record --profile, and trace-cmd\nreport --profile without having to record the data to disk, in between.\n\nThe advantage of using the profile command is that the profiling can be done\nover a long period of time where recording all events would take up too much\ndisk space.\n\nThis will enable several events as well as the function graph tracer\nwith a depth of one (if the kernel supports it). This is to show where\ntasks enter and exit the kernel and how long they were in the kernel.\n\nTo disable calling function graph, use the *-p* option to enable another\ntracer. To not enable any tracer, use *-p nop*.\n\nAll timings are currently in nanoseconds.\n\nOPTIONS\n-------\nThese are the same as trace-cmd-record(1) with the *--profile* option.\n\n*-p* 'tracer'::\n    Set a tracer plugin to run instead of function graph tracing set to\n    depth of 1. To not run any tracer, use *-p nop*.\n\n*-S*::\n    Only enable the tracer or events speficied on the command line.\n    With this option, the function_graph tracer is not enabled, nor are\n    any events (like sched_switch), unless they are specifically specified\n    on the command line (i.e. -p function -e sched_switch -e sched_wakeup)\n\n*-G*::\n    Set interrupt (soft and hard) events as global (associated to CPU\n    instead of tasks).\n\n*-o* 'file'::\n    Write the output of the profile to 'file'. This supersedes *--stderr*\n\n*-H* 'event-hooks'::\n    Add custom event matching to connect any two events together. Format is:\n    [<start_system>:]<start_event>,<start_match>[,<start_pid>]/\n    [<end_system>:]<end_event>,<end_match>[,<flags>]\n\n    The start_system:start_event (start_system is optional), is the event that\n    starts the timing.\n\n    start_match is the field in the start event that is to match with the\n    end_match in the end event.\n\n    start_pid is optional, as matches are attached to the tasks that run\n    the events, if another field should be used to find that task, then\n    it is specified with start_pid.\n\n    end_system:end_event is the event that ends the timing (end_system is\n    optional).\n\n    end_match is the field in end_match that wil match the start event field\n    start_match.\n\n    flags are optional and can be the following (case insensitive):\n\n      p : The two events are pinned to the same CPU (start and end happen\n          on the same CPU always).\n\n      s : The event should have a stack traced with it (enable stack tracing\n          for the start event).\n\n      g : The event is global (not associated to a task). start_pid is\n          not applicable with this flag.\n\n*--stderr*::\n    Redirect the output to stderr. The output of the command being executed\n    is not changed. This allows watching the command execute and saving the\n    output of the profile to another file.\n\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"critical\", \"error\", \"warning\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd profile --verbose=warning\n\nEXAMPLES\n--------\n\n ---\n# trace-cmd profile -F sleep 1\n [..]\ntask: sleep-1121\n  Event: sched_switch:R (2) Total: 234559 Avg: 117279 Max: 129886 Min:104673\n          | \n          + ftrace_raw_event_sched_switch (0xffffffff8109f310)\n              100% (2) time:234559 max:129886 min:104673 avg:117279\n               __schedule (0xffffffff816c1e81)\n               preempt_schedule (0xffffffff816c236e)\n               ___preempt_schedule (0xffffffff81351a59)\n                | \n                + unmap_single_vma (0xffffffff81198c05)\n                |   55% (1) time:129886 max:129886 min:0 avg:129886\n                |    stop_one_cpu (0xffffffff8110909a)\n                |    sched_exec (0xffffffff810a119b)\n                |    do_execveat_common.isra.31 (0xffffffff811de528)\n                |    do_execve (0xffffffff811dea8c)\n                |    SyS_execve (0xffffffff811ded1e)\n                |    return_to_handler (0xffffffff816c8458)\n                |    stub_execve (0xffffffff816c6929)\n                | \n                + unmap_single_vma (0xffffffff81198c05)\n                    45% (1) time:104673 max:104673 min:0 avg:104673\n                     unmap_vmas (0xffffffff81199174)\n                     exit_mmap (0xffffffff811a1f5b)\n                     mmput (0xffffffff8107699a)\n                     flush_old_exec (0xffffffff811ddb75)\n                     load_elf_binary (0xffffffff812287df)\n                     search_binary_handler (0xffffffff811dd3e0)\n                     do_execveat_common.isra.31 (0xffffffff811de8bd)\n                     do_execve (0xffffffff811dea8c)\n                     SyS_execve (0xffffffff811ded1e)\n                     return_to_handler (0xffffffff816c8458)\n                     stub_execve (0xffffffff816c6929)\n                  \n            \n            \n      \n  Event: sched_switch:S (1) Total: 1000513242 Avg: 1000513242 Max: 1000513242 Min:1000513242\n          | \n          + ftrace_raw_event_sched_switch (0xffffffff8109f310)\n              100% (1) time:1000513242 max:1000513242 min:0 avg:1000513242\n               __schedule (0xffffffff816c1e81)\n               schedule (0xffffffff816c23b9)\n               do_nanosleep (0xffffffff816c4f1c)\n               hrtimer_nanosleep (0xffffffff810dcd86)\n               SyS_nanosleep (0xffffffff810dcea6)\n               return_to_handler (0xffffffff816c8458)\n               tracesys_phase2 (0xffffffff816c65b0)\n            \n\n      \n  Event: sched_wakeup:1121 (1) Total: 43405 Avg: 43405 Max: 43405 Min:43405\n          | \n          + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960)\n              100% (1) time:43405 max:43405 min:0 avg:43405\n               ttwu_do_wakeup (0xffffffff810a01a2)\n               ttwu_do_activate.constprop.122 (0xffffffff810a0236)\n               try_to_wake_up (0xffffffff810a3ec3)\n               wake_up_process (0xffffffff810a4057)\n               hrtimer_wakeup (0xffffffff810db772)\n               __run_hrtimer (0xffffffff810dbd91)\n               hrtimer_interrupt (0xffffffff810dc6b7)\n               local_apic_timer_interrupt (0xffffffff810363e7)\n               smp_trace_apic_timer_interrupt (0xffffffff816c8c6a)\n               trace_apic_timer_interrupt (0xffffffff816c725a)\n               finish_task_switch (0xffffffff8109c3a4)\n               __schedule (0xffffffff816c1e01)\n               schedule (0xffffffff816c23b9)\n               ring_buffer_wait (0xffffffff811323a3)\n               wait_on_pipe (0xffffffff81133d93)\n               tracing_buffers_splice_read (0xffffffff811350b0)\n               do_splice_to (0xffffffff8120476f)\n               SyS_splice (0xffffffff81206c1f)\n               tracesys_phase2 (0xffffffff816c65b0)\n            \n      \n  Event: func: sys_nanosleep() (1) Total: 1000598016 Avg: 1000598016 Max: 1000598016 Min:1000598016\n  Event: func: sys_munmap() (1) Total: 14300 Avg: 14300 Max: 14300 Min:14300\n  Event: func: sys_arch_prctl() (1) Total: 571 Avg: 571 Max: 571 Min:571\n  Event: func: sys_mprotect() (4) Total: 14382 Avg: 3595 Max: 7196 Min:2190\n  Event: func: SyS_read() (1) Total: 2640 Avg: 2640 Max: 2640 Min:2640\n  Event: func: sys_close() (5) Total: 4001 Avg: 800 Max: 1252 Min:414\n  Event: func: sys_newfstat() (3) Total: 11684 Avg: 3894 Max: 10206 Min:636\n  Event: func: SyS_open() (3) Total: 23615 Avg: 7871 Max: 10535 Min:4743\n  Event: func: sys_access() (1) Total: 5924 Avg: 5924 Max: 5924 Min:5924\n  Event: func: SyS_mmap() (8) Total: 39153 Avg: 4894 Max: 12354 Min:1518\n  Event: func: smp_trace_apic_timer_interrupt() (1) Total: 10298 Avg: 10298 Max: 10298 Min:10298\n  Event: func: SyS_brk() (4) Total: 2407 Avg: 601 Max: 1564 Min:206\n  Event: func: do_notify_resume() (2) Total: 4095 Avg: 2047 Max: 2521 Min:1574\n  Event: func: sys_execve() (5) Total: 1625251 Avg: 325050 Max: 1605698 Min:3570\n          | \n          + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960)\n              100% (1) time:1605698 max:1605698 min:0 avg:1605698\n               ttwu_do_wakeup (0xffffffff810a01a2)\n               ttwu_do_activate.constprop.122 (0xffffffff810a0236)\n               try_to_wake_up (0xffffffff810a3ec3)\n               wake_up_process (0xffffffff810a4057)\n               cpu_stop_queue_work (0xffffffff81108df8)\n               stop_one_cpu (0xffffffff8110909a)\n               sched_exec (0xffffffff810a119b)\n               do_execveat_common.isra.31 (0xffffffff811de528)\n               do_execve (0xffffffff811dea8c)\n               SyS_execve (0xffffffff811ded1e)\n               return_to_handler (0xffffffff816c8458)\n               stub_execve (0xffffffff816c6929)\n               stub_execve (0xffffffff816c6929)\n            \n      \n  Event: func: syscall_trace_enter_phase2() (38) Total: 21544 Avg: 566 Max: 1066 Min:329\n  Event: func: syscall_trace_enter_phase1() (38) Total: 9202 Avg: 242 Max: 376 Min:150\n  Event: func: __do_page_fault() (53) Total: 257672 Avg: 4861 Max: 27745 Min:458\n          | \n          + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960)\n              100% (1) time:27745 max:27745 min:0 avg:27745\n               ttwu_do_wakeup (0xffffffff810a01a2)\n               ttwu_do_activate.constprop.122 (0xffffffff810a0236)\n               try_to_wake_up (0xffffffff810a3ec3)\n               default_wake_function (0xffffffff810a4002)\n               autoremove_wake_function (0xffffffff810b50fd)\n               __wake_up_common (0xffffffff810b4958)\n               __wake_up (0xffffffff810b4cb8)\n               rb_wake_up_waiters (0xffffffff8112f126)\n               irq_work_run_list (0xffffffff81157d0f)\n               irq_work_run (0xffffffff81157d5e)\n               smp_trace_irq_work_interrupt (0xffffffff810082fc)\n               trace_irq_work_interrupt (0xffffffff816c7aaa)\n               return_to_handler (0xffffffff816c8458)\n               trace_do_page_fault (0xffffffff810478b2)\n               trace_page_fault (0xffffffff816c7dd2)\n            \n      \n  Event: func: syscall_trace_leave() (38) Total: 26145 Avg: 688 Max: 1264 Min:381\n  Event: func: __sb_end_write() (1) Total: 373 Avg: 373 Max: 373 Min:373\n  Event: func: fsnotify() (1) Total: 598 Avg: 598 Max: 598 Min:598\n  Event: func: __fsnotify_parent() (1) Total: 286 Avg: 286 Max: 286 Min:286\n  Event: func: mutex_unlock() (2) Total: 39636 Avg: 19818 Max: 39413 Min:223\n  Event: func: smp_trace_irq_work_interrupt() (6) Total: 236459 Avg: 39409 Max: 100671 Min:634\n          | \n          + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960)\n              100% (4) time:234348 max:100671 min:38745 avg:58587\n               ttwu_do_wakeup (0xffffffff810a01a2)\n               ttwu_do_activate.constprop.122 (0xffffffff810a0236)\n               try_to_wake_up (0xffffffff810a3ec3)\n               default_wake_function (0xffffffff810a4002)\n               autoremove_wake_function (0xffffffff810b50fd)\n               __wake_up_common (0xffffffff810b4958)\n               __wake_up (0xffffffff810b4cb8)\n               rb_wake_up_waiters (0xffffffff8112f126)\n               irq_work_run_list (0xffffffff81157d0f)\n               irq_work_run (0xffffffff81157d5e)\n               smp_trace_irq_work_interrupt (0xffffffff810082fc)\n               return_to_handler (0xffffffff816c8458)\n               trace_irq_work_interrupt (0xffffffff816c7aaa)\n                | \n                + ftrace_return_to_handler (0xffffffff81140840)\n                |   84% (3) time:197396 max:100671 min:38745 avg:65798\n                |    return_to_handler (0xffffffff816c846d)\n                |    trace_page_fault (0xffffffff816c7dd2)\n                | \n                + ftrace_return_to_handler (0xffffffff81140840)\n                    16% (1) time:36952 max:36952 min:0 avg:36952\n                     ftrace_graph_caller (0xffffffff816c8428)\n                     mutex_unlock (0xffffffff816c3f75)\n                     rb_simple_write (0xffffffff81133142)\n                     vfs_write (0xffffffff811d7727)\n                     SyS_write (0xffffffff811d7acf)\n                     tracesys_phase2 (0xffffffff816c65b0)\n                  \n            \n            \n      \n  Event: sys_enter:35 (1) Total: 1000599765 Avg: 1000599765 Max: 1000599765 Min:1000599765\n  Event: sys_enter:11 (1) Total: 55025 Avg: 55025 Max: 55025 Min:55025\n  Event: sys_enter:158 (1) Total: 1584 Avg: 1584 Max: 1584 Min:1584\n  Event: sys_enter:10 (4) Total: 18359 Avg: 4589 Max: 8764 Min:2933\n  Event: sys_enter:0 (1) Total: 4223 Avg: 4223 Max: 4223 Min:4223\n  Event: sys_enter:3 (5) Total: 9948 Avg: 1989 Max: 2606 Min:1203\n  Event: sys_enter:5 (3) Total: 15530 Avg: 5176 Max: 11840 Min:1405\n  Event: sys_enter:2 (3) Total: 28002 Avg: 9334 Max: 12035 Min:5656\n  Event: sys_enter:21 (1) Total: 7814 Avg: 7814 Max: 7814 Min:7814\n  Event: sys_enter:9 (8) Total: 49583 Avg: 6197 Max: 14137 Min:2362\n  Event: sys_enter:12 (4) Total: 108493 Avg: 27123 Max: 104079 Min:922\n  Event: sys_enter:59 (5) Total: 1631608 Avg: 326321 Max: 1607529 Min:4563\n  Event: page_fault_user:0x398d86b630 (1)\n  Event: page_fault_user:0x398d844de0 (1)\n  Event: page_fault_user:0x398d8d9020 (1)\n  Event: page_fault_user:0x1d37008 (1)\n  Event: page_fault_user:0x7f0b89e91074 (1)\n  Event: page_fault_user:0x7f0b89d98ed0 (1)\n  Event: page_fault_user:0x7f0b89ec8950 (1)\n  Event: page_fault_user:0x7f0b89d83644 (1)\n  Event: page_fault_user:0x7f0b89d622a8 (1)\n  Event: page_fault_user:0x7f0b89d5a560 (1)\n  Event: page_fault_user:0x7f0b89d34010 (1)\n  Event: page_fault_user:0x1d36008 (1)\n  Event: page_fault_user:0x398d900510 (1)\n  Event: page_fault_user:0x398dbb3ae8 (1)\n  Event: page_fault_user:0x398d87f490 (1)\n  Event: page_fault_user:0x398d8eb660 (1)\n  Event: page_fault_user:0x398d8bd730 (1)\n  Event: page_fault_user:0x398d9625d9 (1)\n  Event: page_fault_user:0x398d931810 (1)\n  Event: page_fault_user:0x398dbb7114 (1)\n  Event: page_fault_user:0x398d837610 (1)\n  Event: page_fault_user:0x398d89e860 (1)\n  Event: page_fault_user:0x398d8f23b0 (1)\n  Event: page_fault_user:0x398dbb4510 (1)\n  Event: page_fault_user:0x398dbad6f0 (1)\n  Event: page_fault_user:0x398dbb1018 (1)\n  Event: page_fault_user:0x398d977b37 (1)\n  Event: page_fault_user:0x398d92eb60 (1)\n  Event: page_fault_user:0x398d8abff0 (1)\n  Event: page_fault_user:0x398dbb0d30 (1)\n  Event: page_fault_user:0x398dbb6c24 (1)\n  Event: page_fault_user:0x398d821c50 (1)\n  Event: page_fault_user:0x398dbb6c20 (1)\n  Event: page_fault_user:0x398d886350 (1)\n  Event: page_fault_user:0x7f0b90125000 (1)\n  Event: page_fault_user:0x7f0b90124740 (1)\n  Event: page_fault_user:0x7f0b90126000 (1)\n  Event: page_fault_user:0x398d816230 (1)\n  Event: page_fault_user:0x398d8002b8 (1)\n  Event: page_fault_user:0x398dbb0b40 (1)\n  Event: page_fault_user:0x398dbb2880 (1)\n  Event: page_fault_user:0x7f0b90141cc6 (1)\n  Event: page_fault_user:0x7f0b9013b85c (1)\n  Event: page_fault_user:0x7f0b90127000 (1)\n  Event: page_fault_user:0x606e70 (1)\n  Event: page_fault_user:0x7f0b90144010 (1)\n  Event: page_fault_user:0x7fffcb31b038 (1)\n  Event: page_fault_user:0x606da8 (1)\n  Event: page_fault_user:0x400040 (1)\n  Event: page_fault_user:0x398d222218 (1)\n  Event: page_fault_user:0x398d015120 (1)\n  Event: page_fault_user:0x398d220ce8 (1)\n  Event: page_fault_user:0x398d220b80 (1)\n  Event: page_fault_user:0x7fffcb2fcff8 (1)\n  Event: page_fault_user:0x398d001590 (1)\n  Event: page_fault_user:0x398d838490 (1)\n  Event: softirq_raise:RCU (3) Total: 252931 Avg: 84310 Max: 243288 Min:4639\n  Event: softirq_raise:SCHED (2) Total: 241249 Avg: 120624 Max: 239076 Min:2173\n          | \n          + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960)\n              100% (1) time:239076 max:239076 min:0 avg:239076\n               ttwu_do_wakeup (0xffffffff810a01a2)\n               ttwu_do_activate.constprop.122 (0xffffffff810a0236)\n               try_to_wake_up (0xffffffff810a3ec3)\n               default_wake_function (0xffffffff810a4002)\n               autoremove_wake_function (0xffffffff810b50fd)\n               __wake_up_common (0xffffffff810b4958)\n               __wake_up (0xffffffff810b4cb8)\n               rb_wake_up_waiters (0xffffffff8112f126)\n               irq_work_run_list (0xffffffff81157d0f)\n               irq_work_run (0xffffffff81157d5e)\n               smp_trace_irq_work_interrupt (0xffffffff810082fc)\n               trace_irq_work_interrupt (0xffffffff816c7aaa)\n               irq_exit (0xffffffff8107dd66)\n               smp_trace_apic_timer_interrupt (0xffffffff816c8c7a)\n               trace_apic_timer_interrupt (0xffffffff816c725a)\n               prepare_ftrace_return (0xffffffff8103d4fd)\n               ftrace_graph_caller (0xffffffff816c8428)\n               mem_cgroup_begin_page_stat (0xffffffff811cfd25)\n               page_remove_rmap (0xffffffff811a4fc5)\n               stub_execve (0xffffffff816c6929)\n               unmap_single_vma (0xffffffff81198b1c)\n               unmap_vmas (0xffffffff81199174)\n               exit_mmap (0xffffffff811a1f5b)\n               mmput (0xffffffff8107699a)\n               flush_old_exec (0xffffffff811ddb75)\n               load_elf_binary (0xffffffff812287df)\n               search_binary_handler (0xffffffff811dd3e0)\n               do_execveat_common.isra.31 (0xffffffff811de8bd)\n               do_execve (0xffffffff811dea8c)\n               SyS_execve (0xffffffff811ded1e)\n               return_to_handler (0xffffffff816c8458)\n            \n      \n  Event: softirq_raise:HI (3) Total: 72472 Avg: 24157 Max: 64186 Min:3430\n  Event: softirq_entry:RCU (2) Total: 3191 Avg: 1595 Max: 1788 Min:1403\n          | \n          + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960)\n              100% (1) time:1788 max:1788 min:0 avg:1788\n               ttwu_do_wakeup (0xffffffff810a01a2)\n               ttwu_do_activate.constprop.122 (0xffffffff810a0236)\n               try_to_wake_up (0xffffffff810a3ec3)\n               default_wake_function (0xffffffff810a4002)\n               autoremove_wake_function (0xffffffff810b50fd)\n               __wake_up_common (0xffffffff810b4958)\n               __wake_up (0xffffffff810b4cb8)\n               rb_wake_up_waiters (0xffffffff8112f126)\n               irq_work_run_list (0xffffffff81157d0f)\n               irq_work_run (0xffffffff81157d5e)\n               smp_trace_irq_work_interrupt (0xffffffff810082fc)\n               trace_irq_work_interrupt (0xffffffff816c7aaa)\n               irq_work_queue (0xffffffff81157e95)\n               ring_buffer_unlock_commit (0xffffffff8113039f)\n               __buffer_unlock_commit (0xffffffff811367d5)\n               trace_buffer_unlock_commit (0xffffffff811376a2)\n               ftrace_event_buffer_commit (0xffffffff81146d5f)\n               ftrace_raw_event_sched_process_exec (0xffffffff8109c511)\n               do_execveat_common.isra.31 (0xffffffff811de9a3)\n               do_execve (0xffffffff811dea8c)\n               SyS_execve (0xffffffff811ded1e)\n               return_to_handler (0xffffffff816c8458)\n               stub_execve (0xffffffff816c6929)\n             \n      \n  Event: softirq_entry:SCHED (2) Total: 2289 Avg: 1144 Max: 1350 Min:939\n  Event: softirq_entry:HI (3) Total: 180146 Avg: 60048 Max: 178969 Min:499\n          | \n          + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960)\n              100% (1) time:178969 max:178969 min:0 avg:178969\n               ttwu_do_wakeup (0xffffffff810a01a2)\n               ttwu_do_activate.constprop.122 (0xffffffff810a0236)\n               try_to_wake_up (0xffffffff810a3ec3)\n               wake_up_process (0xffffffff810a4057)\n               wake_up_worker (0xffffffff8108de74)\n               insert_work (0xffffffff8108fca6)\n               __queue_work (0xffffffff8108fe12)\n               delayed_work_timer_fn (0xffffffff81090088)\n               call_timer_fn (0xffffffff810d8f89)\n               run_timer_softirq (0xffffffff810da8a1)\n               __do_softirq (0xffffffff8107d8fa)\n               irq_exit (0xffffffff8107dd66)\n               smp_trace_apic_timer_interrupt (0xffffffff816c8c7a)\n               trace_apic_timer_interrupt (0xffffffff816c725a)\n               prepare_ftrace_return (0xffffffff8103d4fd)\n               ftrace_graph_caller (0xffffffff816c8428)\n               mem_cgroup_begin_page_stat (0xffffffff811cfd25)\n               page_remove_rmap (0xffffffff811a4fc5)\n               stub_execve (0xffffffff816c6929)\n               unmap_single_vma (0xffffffff81198b1c)\n               unmap_vmas (0xffffffff81199174)\n               exit_mmap (0xffffffff811a1f5b)\n               mmput (0xffffffff8107699a)\n               flush_old_exec (0xffffffff811ddb75)\n               load_elf_binary (0xffffffff812287df)\n               search_binary_handler (0xffffffff811dd3e0)\n               do_execveat_common.isra.31 (0xffffffff811de8bd)\n               do_execve (0xffffffff811dea8c)\n               SyS_execve (0xffffffff811ded1e)\n               return_to_handler (0xffffffff816c8458)\n ---           \n\nThe above uses *-F* to follow the sleep task. It filters only on events\nthat pertain to sleep. Note, in order to follow forks, you need to also\ninclude the *-c* flag.\n\nOther tasks will appear in the profile as well if events reference more\nthan one task (like sched_switch and sched_wakeup do. The \"prev_pid\" and\n\"next_pid\" of sched_switch, and the \"common_pid\" and \"pid\" of sched_wakeup).\n\nStack traces are attached to events that are related to them.\n\nTaking a look at the above output:\n\n  Event: sched_switch:R (2) Total: 234559 Avg: 117279 Max: 129886 Min:104673\n\nThis shows that task was preempted (it's in the running 'R' state).\nIt was preempted twice '(2)' for a total of 234,559 nanoseconds, with a average\npreempt time of 117,279 ns, and maximum of 128,886 ns and minimum of 104,673 ns.\n\nThe tree shows where it was preempted:\n\n\n          | \n          + ftrace_raw_event_sched_switch (0xffffffff8109f310)\n              100% (2) time:234559 max:129886 min:104673 avg:117279\n               __schedule (0xffffffff816c1e81)\n               preempt_schedule (0xffffffff816c236e)\n               ___preempt_schedule (0xffffffff81351a59)\n                | \n                + unmap_single_vma (0xffffffff81198c05)\n                |   55% (1) time:129886 max:129886 min:0 avg:129886\n                |    stop_one_cpu (0xffffffff8110909a)\n                |    sched_exec (0xffffffff810a119b)\n                |    do_execveat_common.isra.31 (0xffffffff811de528)\n                |    do_execve (0xffffffff811dea8c)\n                |    SyS_execve (0xffffffff811ded1e)\n                |    return_to_handler (0xffffffff816c8458)\n                |    stub_execve (0xffffffff816c6929)\n                | \n                + unmap_single_vma (0xffffffff81198c05)\n                    45% (1) time:104673 max:104673 min:0 avg:104673\n                     unmap_vmas (0xffffffff81199174)\n                     exit_mmap (0xffffffff811a1f5b)\n                     mmput (0xffffffff8107699a)\n                     flush_old_exec (0xffffffff811ddb75)\n                     load_elf_binary (0xffffffff812287df)\n                     search_binary_handler (0xffffffff811dd3e0)\n                     do_execveat_common.isra.31 (0xffffffff811de8bd)\n                     do_execve (0xffffffff811dea8c)\n                     SyS_execve (0xffffffff811ded1e)\n                     return_to_handler (0xffffffff816c8458)\n                     stub_execve (0xffffffff816c6929)\n\n\n  Event: sched_switch:S (1) Total: 1000513242 Avg: 1000513242 Max: 1000513242 Min:10005132\n\nThis shows that the task was scheduled out in the INTERRUPTIBLE state once\nfor a total of 1,000,513,242 ns (~1s), which makes sense as the task was a\n\"sleep 1\".\n\nAfter the schedule events, the function events are shown. By default the\nprofiler will use the function graph tracer if the depth setting is supported\nby the kernel. It will set the depth to one which will only trace the first\nfunction that enters the kernel. It will also record the amount of time\nit was in the kernel.\n\n  Event: func: sys_nanosleep() (1) Total: 1000598016 Avg: 1000598016 Max: 1000598016 Min:1000598016\n  Event: func: sys_munmap() (1) Total: 14300 Avg: 14300 Max: 14300 Min:14300\n  Event: func: sys_arch_prctl() (1) Total: 571 Avg: 571 Max: 571 Min:571\n  Event: func: sys_mprotect() (4) Total: 14382 Avg: 3595 Max: 7196 Min:2190\n  Event: func: SyS_read() (1) Total: 2640 Avg: 2640 Max: 2640 Min:2640\n  Event: func: sys_close() (5) Total: 4001 Avg: 800 Max: 1252 Min:414\n  Event: func: sys_newfstat() (3) Total: 11684 Avg: 3894 Max: 10206 Min:636\n  Event: func: SyS_open() (3) Total: 23615 Avg: 7871 Max: 10535 Min:4743\n  Event: func: sys_access() (1) Total: 5924 Avg: 5924 Max: 5924 Min:5924\n  Event: func: SyS_mmap() (8) Total: 39153 Avg: 4894 Max: 12354 Min:1518\n  Event: func: smp_trace_apic_timer_interrupt() (1) Total: 10298 Avg: 10298 Max: 10298 Min:10298\n  Event: func: SyS_brk() (4) Total: 2407 Avg: 601 Max: 1564 Min:206\n  Event: func: do_notify_resume() (2) Total: 4095 Avg: 2047 Max: 2521 Min:1574\n  Event: func: sys_execve() (5) Total: 1625251 Avg: 325050 Max: 1605698 Min:3570\n\n\nCount of times the event was hit is always in parenthesis '(5)'.\n\nThe function graph trace may produce too much overhead as it is still\ntriggering (just not tracing) on all functions. To limit functions just to\nsystem calls (not interrupts), add the following option:\n\n -l 'sys_*' -l 'SyS_*'\n\nTo disable function graph tracing totally, use:\n\n -p nop\n\nTo use function tracing instead (note, this will not record timings, but just\nthe count of times a function is hit):\n\n -p function\n\n\nFollowing the functions are the events that are recorded.\n\n      \n  Event: sys_enter:35 (1) Total: 1000599765 Avg: 1000599765 Max: 1000599765 Min:1000599765\n  Event: sys_enter:11 (1) Total: 55025 Avg: 55025 Max: 55025 Min:55025\n  Event: sys_enter:158 (1) Total: 1584 Avg: 1584 Max: 1584 Min:1584\n  Event: sys_enter:10 (4) Total: 18359 Avg: 4589 Max: 8764 Min:2933\n  Event: sys_enter:0 (1) Total: 4223 Avg: 4223 Max: 4223 Min:4223\n  Event: sys_enter:3 (5) Total: 9948 Avg: 1989 Max: 2606 Min:1203\n  Event: sys_enter:5 (3) Total: 15530 Avg: 5176 Max: 11840 Min:1405\n  Event: sys_enter:2 (3) Total: 28002 Avg: 9334 Max: 12035 Min:5656\n  Event: sys_enter:21 (1) Total: 7814 Avg: 7814 Max: 7814 Min:7814\n  Event: sys_enter:9 (8) Total: 49583 Avg: 6197 Max: 14137 Min:2362\n  Event: sys_enter:12 (4) Total: 108493 Avg: 27123 Max: 104079 Min:922\n  Event: sys_enter:59 (5) Total: 1631608 Avg: 326321 Max: 1607529 Min:4563\n\nThese are the raw system call events, with the raw system call ID after\nthe \"sys_enter:\"  For example, \"59\" is execve(2). Why did it execute 5 times?\nLooking at a strace of this run, we can see:\n\n execve(\"/usr/lib64/ccache/sleep\", [\"sleep\", \"1\"], [/* 27 vars */] <unfinished ...>\n <... execve resumed> )      = -1 ENOENT (No such file or directory)\n execve(\"/usr/local/sbin/sleep\", [\"sleep\", \"1\"], [/* 27 vars */] <unfinished ...>\n <... execve resumed> )      = -1 ENOENT (No such file or directory)\n execve(\"/usr/local/bin/sleep\", [\"sleep\", \"1\"], [/* 27 vars */] <unfinished ...>\n <... execve resumed> )      = -1 ENOENT (No such file or directory)\n execve(\"/usr/sbin/sleep\", [\"sleep\", \"1\"], [/* 27 vars */] <unfinished ...>\n <... execve resumed> )      = -1 ENOENT (No such file or directory)\n execve(\"/usr/bin/sleep\", [\"sleep\", \"1\"], [/* 27 vars */] <unfinished ...>\n <... execve resumed> )      = 0\n\nIt attempted to execve the \"sleep\" command for each path in $PATH until it found\none.\n\nThe page_fault_user events show what userspace address took a page fault.\n\n  Event: softirq_raise:RCU (3) Total: 252931 Avg: 84310 Max: 243288 Min:4639\n  Event: softirq_raise:SCHED (2) Total: 241249 Avg: 120624 Max: 239076 Min:2173\n          | \n          + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960)\n              100% (1) time:239076 max:239076 min:0 avg:239076\n               ttwu_do_wakeup (0xffffffff810a01a2)\n               ttwu_do_activate.constprop.122 (0xffffffff810a0236)\n               try_to_wake_up (0xffffffff810a3ec3)\n               default_wake_function (0xffffffff810a4002)\n               autoremove_wake_function (0xffffffff810b50fd)\n               __wake_up_common (0xffffffff810b4958)\n               __wake_up (0xffffffff810b4cb8)\n               rb_wake_up_waiters (0xffffffff8112f126)\n               irq_work_run_list (0xffffffff81157d0f)\n               irq_work_run (0xffffffff81157d5e)\n               smp_trace_irq_work_interrupt (0xffffffff810082fc)\n               trace_irq_work_interrupt (0xffffffff816c7aaa)\n               irq_exit (0xffffffff8107dd66)\n\nThe timings for the softirq_raise events measure the time it took from the raised\nsoftirq to the time it executed.\n\nThe timings for the softirq_entry events measure the time the softirq took to\nexecute.\n\nThe stack traces for the softirqs (and possibly other events) are used when\nan event has a stack attached to it. This can happen if the profile ran\nmore stacks than just the sched events, or when events are dropped and\nstacks\n\n\nTo have full control of what gets traced, use the *-S* option that will have\ntrace-cmd not enable any events or the function_graph tracer. Only the events\nlisted on the command line are shown.\n\nIf only the time of kmalloc is needed to be seen, and where it was recorded,\nusing the *-S* option and enabling function_graph and stack tracing for just\nthe function needed will give the profile of only that function.\n\n ---\n# trace-cmd profile -S -p function_graph -l '*kmalloc*' -l '*kmalloc*:stacktrace' sleep 1\ntask: sshd-11786\n  Event: func: __kmalloc_reserve.isra.59() (2) Total: 149684 Avg: 74842 Max: 75598 Min:74086\n          | \n          + __alloc_skb (0xffffffff815a8917)\n          |   67% (2) time:149684 max:75598 min:74086 avg:74842\n          |    __kmalloc_node_track_caller (0xffffffff811c6635)\n          |    __kmalloc_reserve.isra.59 (0xffffffff815a84ac)\n          |    return_to_handler (0xffffffff816c8458)\n          |    sk_stream_alloc_skb (0xffffffff81604ea1)\n          |    tcp_sendmsg (0xffffffff8160592c)\n          |    inet_sendmsg (0xffffffff8162fed1)\n          |    sock_aio_write (0xffffffff8159f9fc)\n          |    do_sync_write (0xffffffff811d694a)\n          |    vfs_write (0xffffffff811d7825)\n          |    SyS_write (0xffffffff811d7adf)\n          |    system_call_fastpath (0xffffffff816c63d2)\n          | \n          + __alloc_skb (0xffffffff815a8917)\n              33% (1) time:74086 max:74086 min:74086 avg:74086\n               __alloc_skb (0xffffffff815a8917)\n               sk_stream_alloc_skb (0xffffffff81604ea1)\n               tcp_sendmsg (0xffffffff8160592c)\n               inet_sendmsg (0xffffffff8162fed1)\n               sock_aio_write (0xffffffff8159f9fc)\n               do_sync_write (0xffffffff811d694a)\n               vfs_write (0xffffffff811d7825)\n               SyS_write (0xffffffff811d7adf)\n               system_call_fastpath (0xffffffff816c63d2)\n [..]\n---            \n\nTo watch the command run but save the output of the profile to a file\nuse --stderr, and redirect stderr to a file\n\n# trace-cmd profile --stderr cyclictest -p 80 -n -t1 2> profile.out\n\nOr simple use *-o*\n\n# trace-cmd profile -o profile.out cyclictest -p 80 -n -t1\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2014 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-record.1.txt",
    "content": "TRACE-CMD-RECORD(1)\n===================\n\nNAME\n----\ntrace-cmd-record - record a trace from the Ftrace Linux internal tracer\n\nSYNOPSIS\n--------\n*trace-cmd record* ['OPTIONS'] ['command']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) record command will set up the Ftrace Linux kernel tracer to\nrecord the specified plugins or events that happen while the 'command'\nexecutes. If no command is given, then it will record until the user hits\nCtrl-C.\n\nThe record command of trace-cmd will set up the Ftrace tracer to start tracing\nthe various events or plugins that are given on the command line. It will then\ncreate a number of tracing processes (one per CPU) that will start recording\nfrom the kernel ring buffer straight into temporary files. When the command is\ncomplete (or Ctrl-C is hit) all the files will be combined into a trace.dat\nfile that can later be read (see trace-cmd-report(1)).\n\nOPTIONS\n-------\n*-p* 'tracer'::\n    Specify a tracer. Tracers usually do more than just trace an event.\n    Common tracers are: *function*, *function_graph*, *preemptirqsoff*,\n    *irqsoff*, *preemptoff* and *wakeup*. A tracer must be supported by the\n    running kernel. To see a list of available tracers, see trace-cmd-list(1).\n\n*-e* 'event'::\n    Specify an event to trace. Various static trace points have been added to\n    the Linux kernel. They are grouped by subsystem where you can enable all\n    events of a given subsystem or specify specific events to be enabled. The\n    'event' is of the format \"subsystem:event-name\". You can also just specify\n    the subsystem without the ':event-name' or the event-name without the\n    \"subsystem:\". Using \"-e sched_switch\" will enable the \"sched_switch\" event\n    where as, \"-e sched\" will enable all events under the \"sched\" subsystem.\n\n    The 'event' can also contain glob expressions. That is, \"*stat*\" will\n    select all events (or subsystems) that have the characters \"stat\" in their\n    names.\n\n    The keyword 'all' can be used to enable all events.\n\n*-a*::\n    Every event that is being recorded has its output format file saved\n    in the output file to be able to display it later. But if other\n    events are enabled in the trace without trace-cmd's knowledge, the\n    formats of those events will not be recorded and trace-cmd report will\n    not be able to display them. If this is the case, then specify the\n    *-a* option and the format for all events in the system will be saved.\n\n*-T*::\n    Enable a stacktrace on each event. For example:\n\n          <idle>-0     [003] 58549.289091: sched_switch:         kworker/0:1:0 [120] R ==> trace-cmd:2603 [120]\n          <idle>-0     [003] 58549.289092: kernel_stack:         <stack trace>\n=> schedule (ffffffff814b260e)\n=> cpu_idle (ffffffff8100a38c)\n=> start_secondary (ffffffff814ab828)\n\n*--func-stack*::\n    Enable a stack trace on all functions. Note this is only applicable\n    for the \"function\" plugin tracer, and will only take effect if the\n    -l option is used and succeeds in limiting functions. If the function\n    tracer is not filtered, and the stack trace is enabled, you can live\n    lock the machine.\n\n*-f* 'filter'::\n    Specify a filter for the previous event. This must come after a *-e*. This\n    will filter what events get recorded based on the content of the event.\n    Filtering is passed to the kernel directly so what filtering is allowed\n    may depend on what version of the kernel you have. Basically, it will\n    let you use C notation to check if an event should be processed or not.\n\n[source,bison]\n----\n    ==, >=, <=, >, <, &, |, && and ||\n----\n\n    The above are usually safe to use to compare fields.\n\n*--no-filter*::\n    Do not filter out the trace-cmd threads. By default, the threads are\n    filtered out to not be traced by events. This option will have the trace-cmd\n    threads also be traced.\n\n*-R* 'trigger'::\n    Specify a trigger for the previous event. This must come after a *-e*. \n    This will add a given trigger to the given event. To only enable the trigger\n    and not the event itself, then place the event after the *-v* option.\n\n    See Documentation/trace/events.txt in the Linux kernel source for more\n    information on triggers.\n\n*-v*::\n    This will cause all events specified after it on the command line to not\n    be traced. This is useful for selecting a subsystem to be traced but to\n    leave out various events. For Example: \"-e sched -v -e \"\\*stat\\*\"\" will\n    enable all events in the sched subsystem except those that have \"stat\" in\n    their names.\n\n    Note: the *-v* option was taken from the way grep(1) inverts the following\n    matches.\n\n*-F*::\n    This will filter only the executable that is given on the command line. If\n    no command is given, then it will filter itself (pretty pointless).\n    Using *-F* will let you trace only events that are caused by the given\n    command.\n\n*-P* 'pid'::\n     Similar to *-F* but lets you specify a process ID to trace.\n\n*-c*::\n     Used with either *-F* (or *-P* if kernel supports it) to trace the process'\n     children too.\n\n*--user*::\n     Execute the specified *command* as given user.\n\n*-C* 'clock'::\n     Set the trace clock to \"clock\".\n\n     Use trace-cmd(1) list -C to see what clocks are available.\n\n*-o* 'output-file'::\n    By default, trace-cmd report will create a 'trace.dat' file. You can\n    specify a different file to write to with the *-o* option.\n\n*-l* 'function-name'::\n    This will limit the 'function' and 'function_graph' tracers to only trace\n    the given function name. More than one *-l* may be specified on the\n    command line to trace more than one function. This supports both full\n    regex(3) parsing, or basic glob parsing. If the filter has only alphanumeric,\n    '_', '*', '?' and '.' characters, then it will be parsed as a basic glob.\n    to force it to be a regex, prefix the filter with '^' or append it with '$'\n    and it will then be parsed as a regex.\n\n*-g* 'function-name'::\n    This option is for the function_graph plugin. It will graph the given\n    function. That is, it will only trace the function and all functions that\n    it calls. You can have more than one *-g* on the command line.\n\n*-n* 'function-name'::\n    This has the opposite effect of *-l*. The function given with the *-n*\n    option will not be traced. This takes precedence, that is, if you include\n    the same function for both *-n* and *-l*, it will not be traced.\n\n*-d*::\n    Some tracer plugins enable the function tracer by default. Like the\n    latency tracers. This option prevents the function tracer from being\n    enabled at start up.\n\n*-D*::\n    The option *-d* will try to use the function-trace option to disable the\n    function tracer (if available), otherwise it defaults to the proc file:\n    /proc/sys/kernel/ftrace_enabled, but will not touch it if the function-trace\n    option is available.  The *-D* option will disable both the ftrace_enabled\n    proc file as well as the function-trace option if it exists.\n\n    Note, this disable function tracing for all users, which includes users\n    outside of ftrace tracers (stack_tracer, perf, etc).\n\n*-O* 'option'::\n    Ftrace has various options that can be enabled or disabled. This allows\n    you to set them. Appending the text 'no' to an option disables it.\n    For example: \"-O nograph-time\" will disable the \"graph-time\" Ftrace\n    option.\n\n*-s* 'interval'::\n    The processes that trace-cmd creates to record from the ring buffer need\n    to wake up to do the recording. Setting the 'interval' to zero will cause\n    the processes to wakeup every time new data is written into the buffer.\n    But since Ftrace is recording kernel activity, the act of this processes\n    going back to sleep may cause new events into the ring buffer which will\n    wake the process back up. This will needlessly add extra data into the\n    ring buffer.\n\n    The 'interval' metric is microseconds. The default is set to 1000 (1 ms).\n    This is the time each recording process will sleep before waking up to\n    record any new data that was written to the ring buffer.\n\n*-r* 'priority'::\n    The priority to run the capture threads at. In a busy system the trace\n    capturing threads may be staved and events can be lost. This increases\n    the priority of those threads to the real time (FIFO) priority.\n    But use this option with care, it can also change the behaviour of\n    the system being traced.\n\n*-b* 'size'::\n    This sets the ring buffer size to 'size' kilobytes. Because the Ftrace\n    ring buffer is per CPU, this size is the size of each per CPU ring buffer\n    inside the kernel. Using \"-b 10000\" on a machine with 4 CPUs will make\n    Ftrace have a total buffer size of 40 Megs.\n\n*--subbuf-size*::\n    The Linux kernel tracing ring buffer is broken up into sub-buffers.\n    These sub-buffers are typically the size of the architecture \"page-size\".\n    (4096 or x86). An event can only be as big as the data portion of a\n    sub-buffer, but in most cases that's not an issue. But the time the\n    writer takes to switch from one sub-buffer to the next has a bit more\n    overhead than adding events within the sub-buffer. By increasing its\n    size, it will allow bigger events (although that is seldom an issue)\n    but also speed up the tracing itself.\n\n    The downside of larger sub-buffers is that a \"read\" of the ring buffer\n    will pull the sub-buffer size out of the ring buffer and replace it\n    with a new sub-buffer. This may not have any real impact, but it may\n    change the behavior slightly. Or it may not!\n\n*-B* 'buffer-name'::\n    If the kernel supports multiple buffers, this will add a buffer with\n    the given name. If the buffer name already exists, that buffer is just\n    reset and will not be deleted at the end of record execution. If the\n    buffer is created, it will be removed at the end of execution (unless\n    the *-k* is set, or 'start' command was used).\n\n    After a buffer name is stated, all events added after that will be\n    associated with that buffer. If no buffer is specified, or an event\n    is specified before a buffer name, it will be associated with the\n    main (toplevel) buffer.\n\n     trace-cmd record -e sched -B block -e block -B time -e timer sleep 1\n\n    The above is will enable all sched events in the main buffer. It will\n    then create a 'block' buffer instance and enable all block events within\n    that buffer. A 'time' buffer instance is created and all timer events\n    will be enabled for that event.\n\n*-m* 'size'::\n    The max size in kilobytes that a per cpu buffer should be. Note, due\n    to rounding to page size, the number may not be totally correct.\n    Also, this is performed by switching between two buffers that are half\n    the given size thus the output may not be of the given size even if\n    much more was written.\n\n    Use this to prevent running out of diskspace for long runs.\n\n*-M* 'cpumask'::\n    Set the cpumask for to trace. It only affects the last buffer instance\n    given. If supplied before any buffer instance, then it affects the\n    main buffer. The value supplied must be a hex number.\n\n     trace-cmd record -p function -M c -B events13 -e all -M 5\n\n    If the -M is left out, then the mask stays the same. To enable all\n    CPUs, pass in a value of '-1'.\n\n*-k*::\n    By default, when trace-cmd is finished tracing, it will reset the buffers\n    and disable all the tracing that it enabled. This option keeps trace-cmd\n    from disabling the tracer and resetting the buffer. This option is useful for\n    debugging trace-cmd.\n\n    Note: usually trace-cmd will set the \"tracing_on\" file back to what it\n    was before it was called. This option will leave that file set to zero.\n\n*-K*::\n    By default, when before trace-cmd starts recording, it will reset the buffers\n    and clear events as well as any triggers and such. This option keeps trace-cmd\n    from doing that. It is similar to *trace-cmd set* but still records data.\n\n*-i*::\n    By default, if an event is listed that trace-cmd does not find, it\n    will exit with an error. This option will just ignore events that are\n    listed on the command line but are not found on the system.\n\n*-N* 'host:port'::\n    If another machine is running \"trace-cmd listen\", this option is used to\n    have the data sent to that machine with UDP packets. Instead of writing\n    to an output file, the data is sent off to a remote box. This is ideal for\n    embedded machines with little storage, or having a single machine that\n    will keep all the data in a single repository.\n\n    Note: This option is not supported with latency tracer plugins:\n      wakeup, wakeup_rt, irqsoff, preemptoff and preemptirqsoff\n\n*-V* 'cid:port'::\n    If recording on a guest VM and the host is running *trace-cmd listen* with\n    the *-V* option as well, or if this is recording on the host, and a guest\n    in running *trace-cmd listen* with the *-V* option, then connect to the\n    listener (the same as connecting with the *-N* option via the network).\n    This has the same limitations as the *-N* option above with respect to\n    latency tracer plugins.\n\n*-t*::\n    This option is used with *-N*, when there's a need to send the live data\n    with TCP packets instead of UDP. Although TCP is not nearly as fast as\n    sending the UDP packets, but it may be needed if the network is not that\n    reliable, the amount of data is not that intensive, and a guarantee is\n    needed that all traced information is transfered successfully.\n\n*-q* | *--quiet*::\n    For use with recording an application. Suppresses normal output\n    (except for errors) to allow only the application's output to be displayed.\n\n*--date*::\n    With the *--date* option, \"trace-cmd\" will write timestamps into the\n    trace buffer after it has finished recording. It will then map the\n    timestamp to gettimeofday which will allow wall time output from the\n    timestamps reading the created 'trace.dat' file.\n\n*--max-graph-depth* 'depth'::\n    Set the maximum depth the function_graph tracer will trace into a function.\n    A value of one will only show where userspace enters the kernel but not any\n    functions called in the kernel. The default is zero, which means no limit.\n\n*--cmdlines-size* 'size'::\n    Set the number of entries the kernel tracing file \"saved_cmdlines\" can\n    contain. This file is a circular buffer which stores the mapping between\n    cmdlines and PIDs. If full, it leads to unresolved cmdlines (\"<...>\") within\n    the trace. The kernel default value is 128.\n\n*--module* 'module'::\n    Filter a module's name in function tracing. It is equivalent to adding\n    ':mod:module' after all other functions being filtered. If no other function\n    filter is listed, then all modules functions will be filtered in the filter.\n\n    '--module snd'  is equivalent to  '-l :mod:snd'\n\n    '--module snd -l \"*jack*\"' is equivalent to '-l \"*jack*:mod:snd\"'\n\n    '--module snd -n \"*\"' is equivalent to '-n :mod:snd'\n\n*--proc-map*::\n     Save the traced process address map into the trace.dat file. The traced\n     processes can be specified using the option *-P*, or as a given 'command'.\n\n*--profile*::\n    With the *--profile* option, \"trace-cmd\" will enable tracing that can\n    be used with trace-cmd-report(1) --profile option. If a tracer *-p* is\n    not set, and function graph depth is supported by the kernel, then\n    the function_graph tracer will be enabled with a depth of one (only\n    show where userspace enters into the kernel). It will also enable\n    various tracepoints with stack tracing such that the report can show\n    where tasks have been blocked for the longest time.\n\n    See trace-cmd-profile(1) for more details and examples.\n\n*-G*::\n    Set interrupt (soft and hard) events as global (associated to CPU\n    instead of tasks). Only works for --profile.\n\n*-H* 'event-hooks'::\n    Add custom event matching to connect any two events together. When not\n    used with *--profile*, it will save the parameter and this will be\n    used by trace-cmd report --profile, too. That is:\n\n    trace-cmd record -H hrtimer_expire_entry,hrtimer/hrtimer_expire_exit,hrtimer,sp\n    trace-cmd report --profile\n\n    Will profile hrtimer_expire_entry and hrtimer_expire_ext times.\n\n    See trace-cmd-profile(1) for format.\n\n*-S*:: (for --profile only)\n    Only enable the tracer or events speficied on the command line.\n    With this option, the function_graph tracer is not enabled, nor are\n    any events (like sched_switch), unless they are specifically specified\n    on the command line (i.e. -p function -e sched_switch -e sched_wakeup)\n\n*--temp* 'directory'::\n    When *trace-cmd* is recording the trace, it records the per CPU data into\n    a separate file for each CPU. At the end of the trace, these files are\n    concatenated onto the final trace.dat file. If the final file is on a network\n    file system, it may not be appropriate to copy these temp files into the\n    same location. *--temp* can be used to tell *trace-cmd* where those temp\n    files should be created.\n\n*--ts-offset offset*::\n    Add an offset for the timestamp in the trace.dat file. This will add a\n    offset option into the trace.dat file such that a trace-cmd report will\n    offset all the timestamps of the events by the given offset. The offset\n    is in raw units. That is, if the event timestamps are in nanoseconds\n    the offset will also be in nanoseconds even if the displayed units are\n    in microseconds.\n\n*--tsync-interval*::\n    Set the loop interval, in ms, for timestamps synchronization with guests:\n        If a negative number is specified, timestamps synchronization is disabled\n        If 0 is specified, no loop is performed - timestamps offset is calculated only twice,\n        at the beginning and at the end of the trace.\n        Timestamps synchronization with guests works only if there is support for VSOCK.\n\n*--tsc2nsec*::\n    Convert the current clock to nanoseconds, using tsc multiplier and shift from the Linux\n        kernel's perf interface. This option does not change the trace clock, just assumes that\n        the tsc multiplier and shift are applicable for the selected clock. You may use the\n        \"-C tsc2nsec\" clock, if not sure what clock to select.\n*--stderr*::\n    Have output go to stderr instead of stdout, but the output of the command\n    executed will not be changed. This is useful if you want to monitor the\n    output of the command being executed, but not see the output from trace-cmd.\n\n*--poll*::\n    Waiting for data to be available on the trace ring-buffers may trigger\n    IPIs. This might generate unacceptable trace noise when tracing low latency\n    or real time systems. The poll option forces trace-cmd to use O_NONBLOCK.\n    Traces are extracted by busy waiting, which will hog the CPUs, so only use\n    when really needed.\n\n*--name*::\n    Give a specific name for the current agent being processed. Used after *-A* to\n    give the guest being traced a name. Useful when using the vsocket ID instead of\n    a name of the guest.\n\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"critical\", \"error\", \"warning\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd record --verbose=warning\n\n*--file-version*::\n    Desired version of the output file. Supported versions are 6 or 7.\n\n*--compression*::\n    Compression of the trace output file, one of these strings can be passed:\n\n    'any'  - auto select the best available compression algorithm\n\n    'none' - do not compress the trace file\n\n    'name' - the name of the desired compression algorithms. Available algorithms can be listed with\n    trace-cmd list -c\n\n*--proxy* 'vsocket'::\n    Use a vsocket proxy to reach the agent. Acts the same as *-A* (for an\n    agent) but will send the proxy connection to the agent. It is expected to\n    run on a privileged guest that the host is aware of (as denoted by the\n    'cid' in the *-P* option for the agent).\n\n*--daemonize*\n    Run trace-cmd in the background as a daemon after recording has started.\n    Creates a pidfile at /var/run/trace-cmd-record.pid with the pid of trace-cmd during the recording.\n\nEXAMPLES\n--------\n\nThe basic way to trace all events:\n\n[source,shell]\n----\n # trace-cmd record -e all ls > /dev/null\n # trace-cmd report\n       trace-cmd-13541 [003] 106260.693809: filemap_fault: address=0x128122 offset=0xce\n       trace-cmd-13543 [001] 106260.693809: kmalloc: call_site=81128dd4 ptr=0xffff88003dd83800 bytes_req=768 bytes_alloc=1024 gfp_flags=GFP_KERNEL|GFP_ZERO\n              ls-13545 [002] 106260.693809: kfree: call_site=810a7abb ptr=0x0\n              ls-13545 [002] 106260.693818: sys_exit_write:       0x1\n----\n\n\n\nTo use the function tracer with sched switch tracing:\n\n[source,shell]\n----\n # trace-cmd record -p function -e sched_switch ls > /dev/null\n # trace-cmd report\n              ls-13587 [002] 106467.860310: function: hrtick_start_fair <-- pick_next_task_fair\n              ls-13587 [002] 106467.860313: sched_switch: prev_comm=trace-cmd prev_pid=13587 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=13583 next_prio=120\n       trace-cmd-13585 [001] 106467.860314: function: native_set_pte_at <-- __do_fault\n       trace-cmd-13586 [003] 106467.860314: function:             up_read <-- do_page_fault\n              ls-13587 [002] 106467.860317: function:             __phys_addr <-- schedule\n       trace-cmd-13585 [001] 106467.860318: function: _raw_spin_unlock <-- __do_fault\n              ls-13587 [002] 106467.860320: function: native_load_sp0 <-- __switch_to\n       trace-cmd-13586 [003] 106467.860322: function: down_read_trylock <-- do_page_fault\n----\n\nHere is a nice way to find what interrupts have the highest latency:\n[source,shell]\n----\n # trace-cmd record -p function_graph -e irq_handler_entry  -l do_IRQ sleep 10\n # trace-cmd report\n          <idle>-0     [000] 157412.933969: funcgraph_entry:                  |  do_IRQ() {\n          <idle>-0     [000] 157412.933974: irq_handler_entry:    irq=48 name=eth0\n          <idle>-0     [000] 157412.934004: funcgraph_exit:       + 36.358 us |  }\n          <idle>-0     [000] 157413.895004: funcgraph_entry:                  |  do_IRQ() {\n          <idle>-0     [000] 157413.895011: irq_handler_entry:    irq=48 name=eth0\n          <idle>-0     [000] 157413.895026: funcgraph_exit:                        + 24.014 us |  }\n          <idle>-0     [000] 157415.891762: funcgraph_entry:                  |  do_IRQ() {\n          <idle>-0     [000] 157415.891769: irq_handler_entry:    irq=48 name=eth0\n          <idle>-0     [000] 157415.891784: funcgraph_exit:       + 22.928 us |  }\n          <idle>-0     [000] 157415.934869: funcgraph_entry:                  |  do_IRQ() {\n          <idle>-0     [000] 157415.934874: irq_handler_entry:    irq=48 name=eth0\n          <idle>-0     [000] 157415.934906: funcgraph_exit:       + 37.512 us |  }\n          <idle>-0     [000] 157417.888373: funcgraph_entry:                  |  do_IRQ() {\n          <idle>-0     [000] 157417.888381: irq_handler_entry:    irq=48 name=eth0\n          <idle>-0     [000] 157417.888398: funcgraph_exit:       + 25.943 us |  }\n----\n\nAn example of the profile:\n[source,shell]\n----\n # trace-cmd record --profile sleep 1\n # trace-cmd report --profile --comm sleep\ntask: sleep-21611\n  Event: sched_switch:R (1) Total: 99442 Avg: 99442 Max: 99442 Min:99442\n     <stack> 1 total:99442 min:99442 max:99442 avg=99442\n       => ftrace_raw_event_sched_switch (0xffffffff8105f812)\n       => __schedule (0xffffffff8150810a)\n       => preempt_schedule (0xffffffff8150842e)\n       => ___preempt_schedule (0xffffffff81273354)\n       => cpu_stop_queue_work (0xffffffff810b03c5)\n       => stop_one_cpu (0xffffffff810b063b)\n       => sched_exec (0xffffffff8106136d)\n       => do_execve_common.isra.27 (0xffffffff81148c89)\n       => do_execve (0xffffffff811490b0)\n       => SyS_execve (0xffffffff811492c4)\n       => return_to_handler (0xffffffff8150e3c8)\n       => stub_execve (0xffffffff8150c699)\n  Event: sched_switch:S (1) Total: 1000506680 Avg: 1000506680 Max: 1000506680 Min:1000506680\n     <stack> 1 total:1000506680 min:1000506680 max:1000506680 avg=1000506680\n       => ftrace_raw_event_sched_switch (0xffffffff8105f812)\n       => __schedule (0xffffffff8150810a)\n       => schedule (0xffffffff815084b8)\n       => do_nanosleep (0xffffffff8150b22c)\n       => hrtimer_nanosleep (0xffffffff8108d647)\n       => SyS_nanosleep (0xffffffff8108d72c)\n       => return_to_handler (0xffffffff8150e3c8)\n       => tracesys_phase2 (0xffffffff8150c304)\n  Event: sched_wakeup:21611 (1) Total: 30326 Avg: 30326 Max: 30326 Min:30326\n     <stack> 1 total:30326 min:30326 max:30326 avg=30326\n       => ftrace_raw_event_sched_wakeup_template (0xffffffff8105f653)\n       => ttwu_do_wakeup (0xffffffff810606eb)\n       => ttwu_do_activate.constprop.124 (0xffffffff810607c8)\n       => try_to_wake_up (0xffffffff8106340a)\n----\n\nAn example of using --daemonize together with guest/host tracing:\n[source,shell]\n----\n$ sudo trace-cmd record --daemonize -p nop -e 'sched:sched_process_exec' -A guest -p nop -e net &&\n> ping -c 1 10.20.1.2 &&\n> sudo start-stop-daemon --stop --signal INT --retry 20 --pidfile /var/run/trace-cmd-record.pid &&\n> sudo trace-cmd report -i trace.dat -i trace-guest.dat | head\nNegotiated kvm time sync protocol with guest guest\nSend SIGINT to pid 3071371 to stop recording\nPING 10.20.1.2 (10.20.1.2) 56(84) bytes of data.\n64 bytes from 10.20.1.2: icmp_seq=1 ttl=64 time=0.134 ms\n--- 10.20.1.2 ping statistics ---\n1 packets transmitted, 1 received, 0% packet loss, time 0ms\nrtt min/avg/max/mdev = 0.134/0.134/0.134/0.000 ms\nCPU0 data recorded at offset=0x14f000\n    229 bytes in size (4096 uncompressed)\n....\n      trace.dat: cpus=28\ntrace-guest.dat: cpus=1\n      trace.dat:           ping-3071450 [013] 1196830.834258: sched_process_exec:     filename=/bin/ping pid=3071450 old_pid=3071450\ntrace-guest.dat:           <idle>-0     [000] 1196830.835990: napi_gro_receive_entry: dev=eth1 napi_id=0x2002 queue_mapping=1 skbaddr=0xffff95d051a5c400 vlan_tagged=0 vlan_proto=0x0000 vlan_tci=0x0000 protocol=0x0800 ip_summed=0 hash=0x00000000 l4_hash=0 len=84 data_len=0 truesize=768 mac_header_valid=1 mac_header=-14 nr_frags=0 gso_size=0 gso_type=0\ntrace-guest.dat:           <idle>-0     [000] 1196830.835997: napi_gro_receive_exit:  ret=3\ntrace-guest.dat:           <idle>-0     [000] 1196830.835998: netif_receive_skb:      dev=eth1 skbaddr=0xffff95d051a5c400x len=84\ntrace-guest.dat:           <idle>-0     [000] 1196830.836021: net_dev_queue:          dev=eth1 skbaddr=0xffff95d051a5c700x len=98\ntrace-guest.dat:           <idle>-0     [000] 1196830.836024: net_dev_start_xmit:     dev=eth1 queue_mapping=0 skbaddr=0xffff95d051a5c700 vlan_tagged=0 vlan_proto=0x0000 vlan_tci=0x0000 protocol=0x0800 ip_summed=0 len=98 data_len=0 network_offset=14 transport_offset_valid=1 transport_offset=34 tx_flags=0 gso_size=0 gso_segs=0 gso_type=0\ntrace-guest.dat:           <idle>-0     [000] 1196830.836069: net_dev_xmit:           dev=eth1 skbaddr=0xffff95d051a5c700 len=98 rc=0\n      trace.dat:           sudo-3071451 [015] 1196830.838262: sched_process_exec:     filename=/usr/bin/sudo pid=3071451 old_pid=3071451\n----\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1), trace-cmd-profile(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-report.1.txt",
    "content": "TRACE-CMD-REPORT(1)\n===================\n\nNAME\n----\ntrace-cmd-report - show in ASCII a trace created by trace-cmd record\n\nSYNOPSIS\n--------\n*trace-cmd report* ['OPTIONS'] ['input-file' ['input-file' ...]]\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) report command will output a human readable report of a trace\ncreated by trace-cmd record.\n\nOPTIONS\n-------\n*-i* 'input-file'::\n    By default, trace-cmd report will read the file 'trace.dat'. But the *-i*\n    option open up the given 'input-file' instead. Note, the input file may\n    also be specified as the last item on the command line.\n\n*-e*::\n    This outputs the endianess of the file. trace-cmd report is smart enough\n    to be able to read big endian files on little endian machines, and vise\n    versa.\n\n*-f*::\n    This outputs the list of all functions that have been mapped in the trace.dat file.\n    Note, this list may contain functions that may not appear in the trace, as\n    it is the list of mappings to translate function addresses into function names.\n\n*-P*::\n    This outputs the list of \"trace_printk()\" data. The raw trace data points\n    to static pointers in the kernel. This must be stored in the trace.dat\n    file.\n\n*-E*::\n    This lists the possible events in the file (but this list is not\n    necessarily the list of events in the file).\n\n*--events*::\n    This will list the event formats that are stored in the trace.dat file.\n\n*--event* regex::\n    This will print events that match the given regex. If a colon is specified,\n    then the characters before the colon will be used to match the system and\n    the characters after the colon will match the event.\n\n     trace-cmd report --event sys:read\n\n    The above will only match events where the system name contains \"sys\"\n    and the event name contains \"read\".\n\n     trace-cmd report --event read\n\n    The above will match all events that contain \"read\" in its name. Also it\n    may list all events of a system that contains \"read\" as well.\n\n*--check-events*::\n    This will parse the event format strings that are stored in the trace.dat\n    file and return whether the formats can be parsed correctly. It will load\n    plugins unless *-N* is specified.\n\n*-t*::\n    Print the full timestamp. The timestamps in the data file are usually\n    recorded to the nanosecond. But the default display of the timestamp\n    is only to the microsecond. To see the full timestamp, add the *-t* option.\n\n*-F* 'filter'::\n    Add a filter to limit what events are displayed.  Filters defined\n    after an input file (specified with *-i*) only apply to that\n    input file. Filters provided before any input file is given are\n    considered global and apply to all input files.\n\n    The format of the filter is:\n\n[source,bison]\n----\n    <events> ':' <filter>\n    <events> = SYSTEM'/'EVENT  | SYSTEM | EVENT | <events> ',' <events>\n    <filter> = EVENT_FIELD <op> <value> | <filter> '&&' <filter> |\n               <filter> '||' <filter> | '(' <filter> ')' | '!' <filter>\n    <op> = '==' | '!=' | '>=' | '<=' | '>' | '<' | '&' | '|' | '^' |\n           '+' | '-' | '*' | '/' | '%'\n    <value> = NUM | STRING | EVENT_FIELD\n----\n\n    SYSTEM is the name of the system to filter on. If the EVENT is left out,\n    then it applies to all events under the SYSTEM. If only one string is used\n    without the '/' to deliminate between SYSTEM and EVENT, then the filter\n    will be applied to all systems and events that match the given string.\n\n    Whitespace is ignored, such that \"sched:next_pid==123\" is equivalent to\n    \"sched : next_pid == 123\".\n\n    STRING is defined with single or double quotes (single quote must end with\n    single quote, and double with double). Whitespace within quotes are not\n    ignored.\n\n    The representation of a SYSTEM or EVENT may also be a regular expression\n    as defined by 'regcomp(3)'.\n\n    The EVENT_FIELD is the name of the field of an event that is being\n    filtered. If the event does not contain the EVENT_FIELD, that part of the\n    equation will be considered false.\n\n[source,shell]\n----\n    -F 'sched : bogus == 1 || common_pid == 2'\n----\n\n    The \"bogus == 1\" will always evaluate to FALSE because no event has a\n    field called \"bogus\", but the \"common_pid == 2\" will still be evaluated\n    since all events have the field \"common_pid\". Any \"sched\" event that was\n    traced by the process with the PID of 2 will be shown.\n\n    Note, the EVENT_FIELD is the field name as shown by an events format\n    (as displayed with *--events*), and not what is found in the output.\n    If the output shows \"ID:foo\" but the field that \"foo\" belongs to was\n    called \"name\" in the event format, then \"name\" must be used in the filter.\n    The same is true about values. If the value that is displayed is converted\n    by to a string symbol, the filter checks the original value and not the\n    value displayed. For example, to filter on all tasks that were in the\n    running state at a context switch:\n\n[source,shell]\n----\n    -F 'sched/sched_switch : prev_state==0'\n----\n\n    Although the output displays 'R', having 'prev_stat==\"R\"' will not work.\n\n    Note: You can also specify 'COMM' as an EVENT_FIELD. This will use the\n    task name (or comm) of the record to compare. For example, to filter out\n    all of the \"trace-cmd\" tasks:\n\n[source,shell]\n----\n    -F '.*:COMM != \"trace-cmd\"'\n----\n\n*-I*::\n    Do not print events where the HARDIRQ latency flag is set.\n    This will filter out most events that are from interrupt context.\n    Note, it may not filter out function traced functions that are\n    in interrupt context but were called before the kernel \"in interrupt\"\n    flag was set.\n\n*-S*::\n    Do not print events where the SOFTIRQ latency flag is set.\n    This will filter out most events that are from soft interrupt context.\n\n*-v*::\n    This causes the following filters of *-F* to filter out the matching\n    events.\n\n[source,shell]\n----\n    -v -F 'sched/sched_switch : prev_state == 0'\n----\n\n    Will not display any sched_switch events that have a prev_state of 0.\n    Removing the *-v* will only print out those events.\n\n*-T*::\n    Test the filters of -F. After processing a filter string, the\n    resulting filter will be displayed for each event. This is useful\n    for using a filter for more than one event where a field may not\n    exist in all events. Also it can be used to make sure there are no\n    misspelled event field names, as they will simply be ignored.\n    *-T* is ignored if *-F* is not specified.\n\n*-V*::\n    Show verbose messages (see *--verbose* but only for the numbers)\n\n*-L*::\n    This will not load system wide plugins. It loads \"local only\". That is\n    what it finds in the ~/.trace-cmd/plugins directory.\n\n*-N*::\n    This will not load any plugins.\n\n*-n* 'event-re'::\n    This will cause all events that match the option to ignore any registered\n    handler (by the plugins) to print the event. The normal event will be printed\n    instead.  The 'event-re' is a regular expression as defined by 'regcomp(3)'.\n\n*--profile*::\n    With the *--profile* option, \"trace-cmd report\" will process all the events\n    first, and then output a format showing where tasks have spent their time\n    in the kernel, as well as where they are blocked the most, and where wake up\n    latencies are.\n\n    See trace-cmd-profile(1) for more details and examples.\n\n*-G*::\n    Set interrupt (soft and hard) events as global (associated to CPU\n    instead of tasks). Only works for --profile.\n\n*-H* 'event-hooks'::\n    Add custom event matching to connect any two events together.\n\n    See trace-cmd-profile(1) for format.\n\n*-R*::\n    This will show the events in \"raw\" format. That is, it will ignore the event's\n    print formatting and just print the contents of each field.\n\n*-r* 'event-re'::\n    This will cause all events that match the option to print its raw fields.\n    The 'event-re' is a regular expression as defined by 'regcomp(3)'.\n\n*-l*::\n    This adds a \"latency output\" format. Information about interrupts being\n    disabled, soft irq being disabled, the \"need_resched\" flag being set,\n    preempt count, and big kernel lock are all being recorded with every\n    event. But the default display does not show this information. This option\n    will set display this information with 6 characters. When one of the\n    fields is zero or N/A a \\'.\\' is shown.\n\n[source,shell]\n----\n      <idle>-0       0d.h1. 106467.859747: function:             ktime_get <-- tick_check_idle\n----\n\n    The 0d.h1. denotes this information.\n\n    It starts with a number. This represents the CPU number that the event occurred\n    on.\n\n    The second character is one of the following:\n\n\t'd' - Interrupts are disabled\n\t'.' - Interrupts are enabled\n\t'X' - Has flags that are not yet known by trace-cmd\n\n    The third character is the \"need rescheduling\" flag.\n\n\t'N' - A schedule is set to take place\n\t'.' - No scheduling is set\n\n    The fourth character represents the context the event was in when it triggered\n\n\t'h' - Hard interrupt context\n\t's' - Soft interrupt context\n\t'H' - Hard interrupt context that interrupted a soft interrupt\n\t'.' - Normal context\n\n    The next is a number (should be less than 10), that represents the preemption\n    depth (the number of times preempt_disable() is called without preempt_enable()).\n    '.' means preemption is enabled.\n\n    On some systems, \"migrate disable\" may exist, in which case a number will be\n    shown for that, or '.' meaning migration is enabled.\n\n    If lockdep in enabled on the system, then the number represents the depth of\n    locks that are held when the event triggered. '.' means no locks are held.\n\n*-w*::\n    If both the 'sched_switch' and 'sched_wakeup' events are enabled, then\n    this option will report the latency between the time the task was first\n    woken, and the time it was scheduled in.\n\n*-q*::\n    Quiet non critical warnings.\n\n*-O*::\n    Pass options to the trace-cmd plugins that are loaded.\n\n     -O plugin:var=value\n\n     The 'plugin:' and '=value' are optional. Value may be left off for options\n     that are boolean. If the 'plugin:' is left off, then any variable that matches\n     in all plugins will be set.\n\n     Example:  -O fgraph:tailprint\n\n*--cpu* <cpu list>::\n    List of CPUs, separated by \",\" or \":\", used for filtering the events.\n    A range of CPUs can be specified using \"cpuX-cpuY\" notation, where all CPUs\n    in the range between cpuX and cpuY will be included in the list. The order\n    of CPUs in the list must be from lower to greater.\n\n    Example:  \"--cpu 0,3\" - show events from CPUs 0 and 3\n              \"--cpu 2-4\" - show events from CPUs 2, 3 and 4\n\n*--cpus*::\n    List the CPUs that have data in the trace file then exit.\n\n*--first-event*::\n    Show the timestamp of the first event of all CPUs that have data.\n\n*--last-event*::\n    Show the timestamp of the last event of all CPUs that have data.\n\n*--stat*::\n    If the trace.dat file recorded the final stats (outputed at the end of record)\n    the *--stat* option can be used to retrieve them.\n\n*--uname*::\n    If the trace.dat file recorded uname during the run, this will retrieve that\n    information.\n\n*--version*::\n    If the trace.dat file recorded the version of the executable used to create\n    it, report that version.\n\n*--ts-offset* offset::\n    Add (or subtract if negative) an offset for all timestamps of the previous\n    data file specified with *-i*. This is useful to merge sort multiple trace.dat\n    files where the difference in the timestamp is known. For example if a trace\n    is done on a virtual guest, and another trace is done on the host. If the\n    host timestamp is 1000 units ahead of the guest, the following can be done:\n\n    trace-cmd report -i host.dat --ts-offset -1000 -i guest.dat\n\n    This will subtract 1000 timestamp units from all the host events as it merges\n    with the guest.dat events. Note, the units is for the raw units recorded in\n    the trace. If the units are nanoseconds, the addition (or subtraction) from\n    the offset will be nanoseconds even if the displayed units are microseconds.\n\n*--ts2secs* HZ::\n    Convert the current clock source into a second (nanosecond resolution)\n    output. When using clocks like x86-tsc, if the frequency is known,\n    by passing in the clock frequency, this will convert the time to seconds.\n\n    This option affects any trace.dat file given with *-i* proceeding it.\n    If this option comes before any *-i* option, then that value becomes\n    the default conversion for all other trace.dat files. If another\n    --ts2secs option appears after a *-i* trace.dat file, than that option\n    will override the default value.\n\n    Example: On a 3.4 GHz machine\n\n      trace-cmd record -p function -C x86-tsc\n\n      trace-cmd report --ts2ns 3400000000\n\n     The report will convert the cycles timestamps into a readable second\n     display. The default display resolution is microseconds, unless *-t*\n     is used.\n\n     The value of --ts-offset must still be in the raw timestamp units, even\n     with this option. The offset will be converted as well.\n\n*--ts-diff*::\n     Show the time differences between events. The difference will appear in\n     parenthesis just after the timestamp.\n\n*--ts-check*::\n     Make sure no timestamp goes backwards, and if it does, print out a warning\n     message of the fact.\n\n*--nodate*::\n     Ignore converting the timestamps to the date set by *trace-cmd record*(3) --date option.\n\n*--raw-ts*::\n     Display raw timestamps, without any corrections.\n\n*--align-ts*::\n     Display timestamps aligned to the first event.\n\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"crit\", \"err\", \"warn\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd report --verbose=warning\n\nEXAMPLES\n--------\n\nUsing a trace.dat file that was created with:\n\n[source,shell]\n----\n    # trace-cmd record -p function -e all sleep 5\n----\n\nThe default report shows:\n\n[source,shell]\n----\n # trace-cmd report\ncpus=8\n           sleep-89142 [001] ...1. 162573.215752: function:             mutex_unlock\n           sleep-89142 [001] ...1. 162573.215754: function:             __mutex_unlock_slowpath\n           sleep-89142 [001] ..... 162573.215755: lock_release:         0xffffffff855e7448 trace_types_lock\n           sleep-89142 [001] ..... 162573.215757: lock_release:         0xffff892a01b54420 sb_writers\n           sleep-89142 [001] ...1. 162573.215757: function:             preempt_count_add\n           sleep-89142 [001] ...1. 162573.215758: preempt_disable:      caller=vfs_write+0x147 parent=vfs_write+0x147\n           sleep-89142 [001] ...2. 162573.215758: function:             rcu_read_lock_any_held\n           sleep-89142 [001] ...2. 162573.215759: function:                rcu_lockdep_current_cpu_online\n           sleep-89142 [001] ...2. 162573.215759: function:             preempt_count_sub\n           sleep-89142 [001] ...1. 162573.215760: preempt_enable:       caller=vfs_write+0x176 parent=vfs_write+0x176\n           sleep-89142 [001] ...1. 162573.215761: function:             __f_unlock_pos\n           sleep-89142 [001] ...1. 162573.215761: function:             mutex_unlock\n[...]\n----\n\nThe note on the third column:\n\n[source,shell]\n----\n           sleep-89998 [002] ...1. 223087.004011: lock_acquire:         0xffff892b7cf32c20 lock\n           sleep-89998 [002] ...1. 223087.004011: lock_acquire:         0xffffffff85517f00 read rcu_read_lock\n          <idle>-0     [005] dNh2. 223087.004012: sched_wakeup:         trace-cmd:89992 [120] CPU:005\n           sleep-89998 [002] ...1. 223087.004012: lock_acquire:         0xffffffff85517f00 read rcu_read_lock\n           sleep-89998 [002] ...1. 223087.004013: lock_release:         0xffffffff85517f00 rcu_read_lock\n----\n\nIt follows the same as shown in the Linux kernel `/sys/kernel/tracing/trace` file.\n\n[source,shell]\n----\n# cat /sys/kernel/tracing/trace\n# tracer: nop\n#\n# entries-in-buffer/entries-written: 0/0   #P:8\n#\n#                                _-----=> irqs-off/BH-disabled\n#                               / _----=> need-resched\n#                              | / _---=> hardirq/softirq\n#                              || / _--=> preempt-depth\n#                              ||| / _-=> migrate-disable\n#                              |||| /     delay\n#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION\n#              | |         |   |||||     |         |\n----\n\nIs the same as explained in the *-l* option.\nWhere the first position is:\n\n  '.' - means interrupts and bottom halves enabled\n  'd' - means interrupts and bottom halves are disabled\n\nThe second position:\n\n   'N' - means that the \"NEED_RESCHED\" flag is set and the kernel should try to\n         schedule as soon as possible.\n\nThe third position:\n\n   '.' - In normal/schedulable context\n   's' - In soft interrupt context\n   'h' - In hard interrupt context\n   'H' - in hard interrupt context that interrupted a soft interrupt\n\nThe forth position is the preempt count depth:\n\n   'pass:[.]' - preemption is enabled\n   '#' - the depth of preemption disabled (nested)\n\nThe fifth column is the migration disabled counter:\n\n   '.' - migration is enabled\n   '#' - the depth of migration being disabled (nested)\n\n\nTo see everything but the function traces:\n\n[source,shell]\n----\n # trace-cmd report -v -F 'function'\ncpus=8\n           sleep-89142 [001] ..... 162573.215755: lock_release:         0xffffffff855e7448 trace_types_lock\n           sleep-89142 [001] ..... 162573.215757: lock_release:         0xffff892a01b54420 sb_writers\n           sleep-89142 [001] ...1. 162573.215758: preempt_disable:      caller=vfs_write+0x147 parent=vfs_write+0x147\n           sleep-89142 [001] ...1. 162573.215760: preempt_enable:       caller=vfs_write+0x176 parent=vfs_write+0x176\n           sleep-89142 [001] ..... 162573.215762: lock_release:         0xffff892a19601ac8 &f->f_pos_lock\n           sleep-89142 [001] ..... 162573.215764: sys_exit:             NR 1 = 1\n           sleep-89142 [001] ..... 162573.215766: sys_exit_write:       0x1\n           sleep-89142 [001] d.... 162573.215767: irq_disable:          caller=syscall_exit_to_user_mode+0x15 parent=0x0\n           sleep-89142 [001] d.... 162573.215768: irq_enable:           caller=syscall_exit_to_user_mode+0xed parent=0x0\n           sleep-89142 [001] ..... 162573.215773: lock_acquire:         0xffff892a4ad29318 read &mm->mmap_lock\n           sleep-89142 [001] ..... 162573.215775: lock_release:         0xffff892a4ad29318 &mm->mmap_lock\n           sleep-89142 [001] ..... 162573.215778: lock_acquire:         0xffff892a4ad29318 read &mm->mmap_lock\n[...]\n----\n\nTo see only the kmalloc calls that were greater than 1000 bytes:\n\n[source,shell]\n----\n # trace-cmd report -F 'kmalloc: bytes_req > 1000'\ncpus=8\n           sleep-89142 [001] ..... 162573.219401: kmalloc:              (tomoyo_find_next_domain+0x84) call_site=tomoyo_find_next_domain+0x84 ptr=0xffff892a176c0000 bytes_req=4096 bytes_alloc=4096 gfp_flags=0xd40 node=-1 accounted=false\n           sleep-89142 [001] ..... 162573.219511: kmalloc:              (tomoyo_realpath_from_path+0x42) call_site=tomoyo_realpath_from_path+0x42 ptr=0xffff892a176c6000 bytes_req=4096 bytes_alloc=4096 gfp_flags=0xc40 node=-1 accounted=false\n       trace-cmd-89135 [000] ..... 162573.244301: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a63f84000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false\n       trace-cmd-89135 [000] ..... 162573.244471: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a63f84000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false\n       trace-cmd-89134 [007] ..... 162573.267148: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a628d4000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false\n       trace-cmd-89134 [007] ..... 162573.267403: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a628d4000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false\n       trace-cmd-89141 [002] ..... 162573.290583: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a12d3c000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false\n       trace-cmd-89141 [002] ..... 162573.290754: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a12d3c000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false\n       trace-cmd-89139 [004] ..... 162573.784636: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a63d70000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false\n[...]\n----\n\nTo see wakeups and sched switches that left the previous task in the running\nstate:\n[source,shell]\n----\n # trace-cmd report -F 'sched: prev_state == 0' -F 'sched_waking'\ncpus=8\n           sleep-89142 [001] d.h6. 162573.215941: sched_waking:         comm=trace-cmd pid=89135 prio=120 target_cpu=000\n          <idle>-0     [000] dNh7. 162573.216219: sched_waking:         comm=trace-cmd pid=89134 prio=120 target_cpu=007\n          <idle>-0     [000] d..2. 162573.216423: sched_switch:         swapper/0:0 [120] R ==> trace-cmd:89135 [120]\n          <idle>-0     [007] dNh7. 162573.216511: sched_waking:         comm=trace-cmd pid=89141 prio=120 target_cpu=002\n          <idle>-0     [007] d..2. 162573.216698: sched_switch:         swapper/7:0 [120] R ==> trace-cmd:89134 [120]\n          <idle>-0     [002] dNh7. 162573.216776: sched_waking:         comm=trace-cmd pid=89136 prio=120 target_cpu=001\n          <idle>-0     [002] d..2. 162573.216951: sched_switch:         swapper/2:0 [120] R ==> trace-cmd:89141 [120]\n           sleep-89142 [001] d.s3. 162573.231260: sched_waking:         comm=rcu_preempt pid=17 prio=120 target_cpu=002\n          <idle>-0     [002] d..2. 162573.231568: sched_switch:         swapper/2:0 [120] R ==> rcu_preempt:17 [120]\n           sleep-89142 [001] d.s2. 162573.240425: sched_waking:         comm=rcu_preempt pid=17 prio=120 target_cpu=002\n          <idle>-0     [002] d..2. 162573.240719: sched_switch:         swapper/2:0 [120] R ==> rcu_preempt:17 [120]\n           sleep-89142 [001] d.h7. 162573.241983: sched_waking:         comm=trace-cmd pid=89135 prio=120 target_cpu=000\n----\n\nThe above needs a little explanation. The filter specifies the \"sched\"\nsubsystem, which includes all scheduling events. Any event\nthat does not have the format field \"prev_state\", will evaluate\nthose expressions as FALSE, and will not produce a match. Only the sched_switch\nevent will match that. The second \"-F\" will include the sched_waking event.\n\n\n[source,shell]\n----\n  # trace-cmd report -w -F 'sched_switch, sched_wakeup.*'\n[...]\n       trace-cmd-89141 [007] d..2. 162583.263060: sched_switch:         trace-cmd:89141 [120] R ==> trace-cmd:89135 [120]\n   kworker/u36:1-51219 [000] d..2. 162583.266957: sched_switch:         kworker/u36:1:51219 [120] R ==> kworker/u33:2:49692 [120] Latency: 4024.977 usecs\n       trace-cmd-89135 [007] d..2. 162583.267109: sched_switch:         trace-cmd:89135 [120] R ==> trace-cmd:89141 [120]\n       trace-cmd-89139 [001] d..2. 162583.267147: sched_switch:         trace-cmd:89139 [120] D ==> swapper/1:0 [120]\n   kworker/u36:2-88857 [002] d..2. 162583.267913: sched_switch:         kworker/u36:2:88857 [120] R ==> trace-cmd:89136 [120]\n   kworker/u33:2-49692 [000] d..2. 162583.268334: sched_switch:         kworker/u33:2:49692 [120] I ==> kworker/u36:1:51219 [120]\n          <idle>-0     [001] dNh4. 162583.268747: sched_wakeup:         sleep:89142 [120] CPU:001\n          <idle>-0     [001] d..2. 162583.268833: sched_switch:         swapper/1:0 [120] R ==> sleep:89142 [120] Latency: 85.751 usecs\n           sleep-89142 [001] d.h4. 162583.269022: sched_wakeup:         trace-cmd:89139 [120] CPU:001\n       trace-cmd-89141 [007] d..2. 162583.271009: sched_switch:         trace-cmd:89141 [120] R ==> trace-cmd:89135 [120]\n       trace-cmd-89136 [002] d..2. 162583.271020: sched_switch:         trace-cmd:89136 [120] R ==> kworker/u36:2:88857 [120]\n   kworker/u36:2-88857 [002] d..2. 162583.271079: sched_switch:         kworker/u36:2:88857 [120] I ==> trace-cmd:89136 [120]\n       trace-cmd-89137 [006] d.h2. 162583.273950: sched_wakeup:         trace-cmd:89133 [120] CPU:006\n           sleep-89142 [001] d..2. 162583.274064: sched_switch:         sleep:89142 [120] Z ==> trace-cmd:89139 [120] Latency: 5042.285 usecs\n       trace-cmd-89135 [007] d..2. 162583.275043: sched_switch:         trace-cmd:89135 [120] R ==> trace-cmd:89141 [120]\n       trace-cmd-89137 [006] d..2. 162583.275158: sched_switch:         trace-cmd:89137 [120] R ==> trace-cmd:89133 [120] Latency: 1207.327 usecs\n       trace-cmd-89136 [002] dNh3. 162583.275229: sched_wakeup:         rcu_preempt:17 [120] CPU:002\n       trace-cmd-89136 [002] d..2. 162583.275294: sched_switch:         trace-cmd:89136 [120] R ==> rcu_preempt:17 [120] Latency: 65.255 usecs\n     rcu_preempt-17    [002] d..2. 162583.275399: sched_switch:         rcu_preempt:17 [120] I ==> trace-cmd:89136 [120]\n\nAverage wakeup latency: 20082.580 usecs\nMaximum Latency: 1032049.277 usecs at timestamp: 162574.787022\nMinimum Latency: 29.023 usecs at timestamp: 162583.189731\n\nRT task timings:\n\nAverage wakeup latency: 139.568 usecs\nMaximum Latency: 220.583 usecs at timestamp: 162577.347038\nMinimum Latency: 75.902 usecs at timestamp: 162577.719121\n----\n\nThe above trace produces the wakeup latencies of the tasks. The \"sched_switch\"\nevent reports each individual latency after writing the event information.\nAt the end of the report, the average wakeup latency is reported, as well\nas the maxim and minimum latency and the timestamp they happened at. It does\nthis for both normal tasks as well as real-time tasks.\n\n[source,shell]\n----\n  # trace-cmd report -w -F 'sched_switch, sched_wakeup.*: prio < 100 || next_prio < 100'\ncpus=8\n          <idle>-0     [001] dNh5. 162573.291142: sched_wakeup:         migration/1:23 [0] CPU:001\n          <idle>-0     [001] d..2. 162573.291237: sched_switch:         swapper/1:0 [120] R ==> migration/1:23 [0] Latency: 94.643 usecs\n       trace-cmd-89141 [002] dNh6. 162573.346785: sched_wakeup:         migration/2:28 [0] CPU:002\n       trace-cmd-89141 [002] d..2. 162573.346929: sched_switch:         trace-cmd:89141 [120] R ==> migration/2:28 [0] Latency: 143.971 usecs\n       trace-cmd-89134 [003] dNh4. 162573.410852: sched_wakeup:         migration/3:33 [0] CPU:003\n       trace-cmd-89134 [003] d..2. 162573.411039: sched_switch:         trace-cmd:89134 [120] R ==> migration/3:33 [0] Latency: 187.640 usecs\n          <idle>-0     [004] dNh5. 162573.490944: sched_wakeup:         migration/4:38 [0] CPU:004\n          <idle>-0     [004] d..2. 162573.491098: sched_switch:         swapper/4:0 [120] R ==> migration/4:38 [0] Latency: 153.913 usecs\n          <idle>-0     [005] dNh5. 162573.574955: sched_wakeup:         migration/5:43 [0] CPU:005\n          <idle>-0     [005] d..2. 162573.575107: sched_switch:         swapper/5:0 [120] R ==> migration/5:43 [0] Latency: 152.875 usecs\n          <idle>-0     [006] dNh5. 162573.646878: sched_wakeup:         migration/6:48 [0] CPU:006\n          <idle>-0     [006] d..2. 162573.646992: sched_switch:         swapper/6:0 [120] R ==> migration/6:48 [0] Latency: 113.788 usecs\n       trace-cmd-89140 [002] dNh7. 162577.346818: sched_wakeup:         migration/2:28 [0] CPU:002\n       trace-cmd-89140 [002] d..2. 162577.347038: sched_switch:         trace-cmd:89140 [120] R ==> migration/2:28 [0] Latency: 220.583 usecs\n       trace-cmd-89134 [003] dNh5. 162577.410869: sched_wakeup:         migration/3:33 [0] CPU:003\n       trace-cmd-89141 [005] dNh6. 162577.574792: sched_wakeup:         migration/5:43 [0] CPU:005\n       trace-cmd-89141 [005] d..2. 162577.574915: sched_switch:         trace-cmd:89141 [120] R ==> migration/5:43 [0] Latency: 122.648 usecs\n       trace-cmd-89136 [007] dNh6. 162577.719045: sched_wakeup:         migration/7:53 [0] CPU:007\n       trace-cmd-89136 [007] d..2. 162577.719121: sched_switch:         trace-cmd:89136 [120] R ==> migration/7:53 [0] Latency: 75.902 usecs\n       trace-cmd-89140 [005] dNh4. 162581.574827: sched_wakeup:         migration/5:43 [0] CPU:005\n       trace-cmd-89140 [005] d..2. 162581.574957: sched_switch:         trace-cmd:89140 [120] R ==> migration/5:43 [0] Latency: 129.717 usecs\n   kworker/u46:1-51211 [006] dNh4. 162581.646809: sched_wakeup:         migration/6:48 [0] CPU:006\n\nAverage wakeup latency: 139.568 usecs\nMaximum Latency: 220.583 usecs at timestamp: 162577.347038\nMinimum Latency: 75.902 usecs at timestamp: 162577.719121\n\nRT task timings:\n\nAverage wakeup latency: 139.568 usecs\nMaximum Latency: 220.583 usecs at timestamp: 162577.347038\nMinimum Latency: 75.902 usecs at timestamp: 162577.719121\n----\n\nThe above version will only show the wakeups and context switches of Real Time\ntasks. The 'prio' used inside the kernel starts at 0 for highest priority.\nThat is 'prio' 0 is equivalent to user space real time priority 99, and\npriority 98 is equivalent to user space real time priority 1.\nPrios less than 100 represent Real Time tasks. Notice that the total wake up timings\nare identical to the RT task timings.\n\nAn example of the profile:\n[source,shell]\n----\n # trace-cmd record --profile sleep 1\n # trace-cmd report --profile --comm sleep\ntask: sleep-21611\n  Event: sched_switch:R (1) Total: 99442 Avg: 99442 Max: 99442 Min:99442\n     <stack> 1 total:99442 min:99442 max:99442 avg=99442\n       => ftrace_raw_event_sched_switch (0xffffffff8105f812)\n       => __schedule (0xffffffff8150810a)\n       => preempt_schedule (0xffffffff8150842e)\n       => ___preempt_schedule (0xffffffff81273354)\n       => cpu_stop_queue_work (0xffffffff810b03c5)\n       => stop_one_cpu (0xffffffff810b063b)\n       => sched_exec (0xffffffff8106136d)\n       => do_execve_common.isra.27 (0xffffffff81148c89)\n       => do_execve (0xffffffff811490b0)\n       => SyS_execve (0xffffffff811492c4)\n       => return_to_handler (0xffffffff8150e3c8)\n       => stub_execve (0xffffffff8150c699)\n  Event: sched_switch:S (1) Total: 1000506680 Avg: 1000506680 Max: 1000506680 Min:1000506680\n     <stack> 1 total:1000506680 min:1000506680 max:1000506680 avg=1000506680\n       => ftrace_raw_event_sched_switch (0xffffffff8105f812)\n       => __schedule (0xffffffff8150810a)\n       => schedule (0xffffffff815084b8)\n       => do_nanosleep (0xffffffff8150b22c)\n       => hrtimer_nanosleep (0xffffffff8108d647)\n       => SyS_nanosleep (0xffffffff8108d72c)\n       => return_to_handler (0xffffffff8150e3c8)\n       => tracesys_phase2 (0xffffffff8150c304)\n  Event: sched_wakeup:21611 (1) Total: 30326 Avg: 30326 Max: 30326 Min:30326\n     <stack> 1 total:30326 min:30326 max:30326 avg=30326\n       => ftrace_raw_event_sched_wakeup_template (0xffffffff8105f653)\n       => ttwu_do_wakeup (0xffffffff810606eb)\n       => ttwu_do_activate.constprop.124 (0xffffffff810607c8)\n       => try_to_wake_up (0xffffffff8106340a)\n----\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-start(1), trace-cmd-stop(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1), trace-cmd-profile(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-reset.1.txt",
    "content": "TRACE-CMD-RESET(1)\n==================\n\nNAME\n----\ntrace-cmd-reset - turn off all Ftrace tracing to bring back full performance\n\nSYNOPSIS\n--------\n*trace-cmd reset* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) reset command turns off all tracing of Ftrace. This will\nbring back the performance of the system before tracing was enabled. This is\nnecessary since 'trace-cmd-record(1)', 'trace-cmd-stop(1)' and\n'trace-cmd-extract(1)' do not disable the tracer, event after the data has\nbeen pulled from the buffers. The rational is that the user may want to\nmanually enable the tracer with the Ftrace pseudo file system, or examine other\nparts of Ftrace to see what trace-cmd did. After the reset command happens,\nthe data in the ring buffer, and the options that were used are all lost.\n\nOPTIONS\n-------\nPlease note that the order that options are specified on the command line is\nsignificant. See EXAMPLES.\n\n*-b* 'buffer_size'::\n    When the kernel boots, the Ftrace ring buffer is of a minimal size (3\n    pages per CPU). The first time the tracer is used, the ring buffer size\n    expands to what it was set for (default 1.4 Megs per CPU).\n\n    If no more tracing is to be done, this option allows you to shrink the\n    ring buffer down to free up available memory.\n\n    trace-cmd reset -b 1\n\n    The buffer instance affected is the one (or ones) specified by the most\n    recently preceding *-B*, *-t*, or *-a* option:\n\n    When used after *-B*, resizes the buffer instance that precedes it on\n    the command line.\n\n    When used after *-a*, resizes all buffer instances except the top one.\n\n    When used after *-t* or before any *-B* or *-a*, resizes the top\n    instance.\n\n*-B* 'buffer-name'::\n    If the kernel supports multiple buffers, this will reset the trace for\n    only the given buffer. It does not affect any other buffer. This may be\n    used multiple times to specify different buffers. The top level buffer\n    will not be reset if this option is given (unless the *-t* option is\n    also supplied).\n\n*-a*::\n    Reset the trace for all existing buffer instances. When this option\n    is used, the top level instance will not be reset unless *-t* is given.\n\n*-d*::\n    This option deletes the instance buffer(s) specified by the most recently\n    preceding *-B* or *-a* option. Because the top-level instance buffer\n    cannot be deleted, it is invalid to use this immediately following *-t* or\n    prior to any *-B* or *-a* option on the command line.\n\n*-t*::\n    Resets the top level instance buffer. Without the *-B* or *-a* option\n    this is the same as the default. But if *-B* or *-a* is used, this is\n    required if the top level instance buffer should also be reset.\n\n*-k* 'dynevent-name'::\n    This option allows preserving specified dynamic event during reset. Valid\n    parameters are *kprobe*, *kretprobe*, *uprobe*, *uretprobe*, *eprobe*,\n    *synth* and *all* (for keeping all dynamic events). This may be used\n    multiple times to specify different dynamic event types.\n\nEXAMPLES\n--------\n\nReset tracing for instance-one and set its per-cpu buffer size to 4096kb.\nAlso deletes instance-two. The top level instance and any other instances\nremain unaffected:\n\n    trace-cmd reset -B instance-one -b 4096 -B instance-two -d\n\nDelete all instance buffers. Top level instance remains unaffected:\n\n    trace-cmd reset -a -d\n\nDelete all instance buffers and also reset the top instance:\n\n    trace-cmd reset -t -a -d\n\nInvalid. This command implies an attempt to delete the top instance:\n\n    trace-cmd reset -a -t -d\n\nReset the top instance and set its per-cpu buffer size to 1024kb. If any\ninstance buffers exist, they will be unaffected:\n\n    trace-cmd reset -b 1024\n\nPrevent *kprobes* and *kretprobes* from being destroyed during reset:\n\n    trace-cmd reset -k kprobe -k kretprobe\n\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-restore.1.txt",
    "content": "TRACE-CMD-RESTORE(1)\n====================\n\nNAME\n----\ntrace-cmd-restore - restore a failed trace record\n\nSYNOPSIS\n--------\n*trace-cmd restore* ['OPTIONS'] ['command'] cpu-file [cpu-file ...]\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) restore command will restore a crashed trace-cmd-record(1)\nfile. If for some reason a trace-cmd record fails, it will leave a the\nper-cpu data files and not create the final trace.dat file. The trace-cmd\nrestore will append the files to create a working trace.dat file that can\nbe read with trace-cmd-report(1).\n\nWhen trace-cmd record runs, it spawns off a process per CPU and writes\nto a per cpu file usually called 'trace.dat.cpuX', where X represents the\nCPU number that it is tracing. If the -o option was used in the trace-cmd\nrecord, then the CPU data files will have that name instead of the\n'trace.dat' name. If a unexpected crash occurs before the tracing\nis finished, then the per CPU files will still exist but there will\nnot be any trace.dat file to read from. trace-cmd restore will allow you\nto create a trace.dat file with the existing data files.\n\nOPTIONS\n-------\n*-c*::\n    Create a partial trace.dat file from the machine, to be used with\n    a full trace-cmd restore at another time. This option is useful for\n    embedded devices. If a server contains the cpu files of a crashed\n    trace-cmd record (or trace-cmd listen), trace-cmd restore can be\n    executed on the embedded device with the -c option to get all the\n    stored information of that embedded device. Then the file created\n    could be copied to the server to run the trace-cmd restore there\n    with the cpu files.\n\n    If *-o* is not specified, then the file created will be called\n    'trace-partial.dat'. This is because the file is not a full version\n    of something that trace-cmd-report(1) could use.\n\n*-t* tracing_dir::\n    Used with *-c*, it overrides the location to read the events from.\n    By default, tracing information is read from the debugfs/tracing\n    directory. *-t* will use that location instead. This can be useful\n    if the trace.dat file to create is from another machine.\n    Just tar -cvf events.tar debugfs/tracing and copy and untar that\n    file locally, and use that directory instead.\n\n*-k* kallsyms::\n    Used with *-c*, it overrides where to read the kallsyms file from.\n    By default, /proc/kallsyms is used. *-k* will override the file to\n    read the kallsyms from. This can be useful if the trace.dat file\n    to create is from another machine. Just copy the /proc/kallsyms\n    file locally, and use *-k* to point to that file.\n\n*-o* output'::\n    By default, trace-cmd restore will create a 'trace.dat' file\n    (or 'trace-partial.dat' if *-c* is specified). You can\n    specify a different file to write to with the *-o* option.\n\n*-i* input::\n    By default, trace-cmd restore will read the information of the\n    current system to create the initial data stored in the 'trace.dat'\n    file. If the crash was on another machine, then that machine should\n    have the trace-cmd restore run with the *-c* option to create the\n    trace.dat partial file. Then that file can be copied to the current\n    machine where trace-cmd restore will use *-i* to load that file\n    instead of reading from the current system.\n\nEXAMPLES\n--------\n\nIf a crash happened on another box, you could run:\n\n   $ trace-cmd restore -c -o box-partial.dat\n\nThen on the server that has the cpu files:\n\n   $ trace-cmd restore -i box-partial.dat trace.dat.cpu0 trace.dat.cpu1\n\nThis would create a trace.dat file for the embedded box.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-set.1.txt",
    "content": "TRACE-CMD-SET(1)\n================\n\nNAME\n----\ntrace-cmd-set - set a configuration parameter of the Ftrace Linux internal tracer\n\nSYNOPSIS\n--------\n*trace-cmd set* ['OPTIONS'] ['command']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) set command will set a configuration parameter of the Ftrace\nLinux kernel tracer. The specified *command* will be run after the ftrace state\nis set. The configured ftrace state can be restored to default\nusing the trace-cmd-reset(1) command.\n\nOPTIONS\n-------\n*-p* 'tracer'::\n    Specify a tracer. Tracers usually do more than just trace an event.\n    Common tracers are: *function*, *function_graph*, *preemptirqsoff*,\n    *irqsoff*, *preemptoff* and *wakeup*. A tracer must be supported by the\n    running kernel. To see a list of available tracers, see trace-cmd-list(1).\n\n*-e* 'event'::\n    Specify an event to trace. Various static trace points have been added to\n    the Linux kernel. They are grouped by subsystem where you can enable all\n    events of a given subsystem or specify specific events to be enabled. The\n    'event' is of the format \"subsystem:event-name\". You can also just specify\n    the subsystem without the ':event-name' or the event-name without the\n    \"subsystem:\". Using \"-e sched_switch\" will enable the \"sched_switch\" event\n    where as, \"-e sched\" will enable all events under the \"sched\" subsystem.\n\n    The 'event' can also contain glob expressions. That is, \"*stat*\" will\n    select all events (or subsystems) that have the characters \"stat\" in their\n    names.\n\n    The keyword 'all' can be used to enable all events.\n\n*-T*::\n    Enable a stacktrace on each event. For example:\n\n          <idle>-0     [003] 58549.289091: sched_switch:         kworker/0:1:0 [120] R ==> trace-cmd:2603 [120]\n          <idle>-0     [003] 58549.289092: kernel_stack:         <stack trace>\n=> schedule (ffffffff814b260e)\n=> cpu_idle (ffffffff8100a38c)\n=> start_secondary (ffffffff814ab828)\n\n*--func-stack*::\n    Enable a stack trace on all functions. Note this is only applicable\n    for the \"function\" plugin tracer, and will only take effect if the\n    -l option is used and succeeds in limiting functions. If the function\n    tracer is not filtered, and the stack trace is enabled, you can live\n    lock the machine.\n\n*-f* 'filter'::\n    Specify a filter for the previous event. This must come after a *-e*. This\n    will filter what events get recorded based on the content of the event.\n    Filtering is passed to the kernel directly so what filtering is allowed\n    may depend on what version of the kernel you have. Basically, it will\n    let you use C notation to check if an event should be processed or not.\n\n[source,bison]\n----\n    ==, >=, <=, >, <, &, |, && and ||\n----\n\n    The above are usually safe to use to compare fields.\n\n*-R* 'trigger'::\n    Specify a trigger for the previous event. This must come after a *-e*.\n    This will add a given trigger to the given event. To only enable the trigger\n    and not the event itself, then place the event after the *-v* option.\n\n    See Documentation/trace/events.txt in the Linux kernel source for more\n    information on triggers.\n\n*-v*::\n    This will negate options specified after it on the command line. It affects:\n[verse]\n--\n     *-e*: Causes all specified events to not be traced. This is useful for\n           selecting a subsystem to be traced but to leave out various events.\n           For example: \"-e sched -v -e \"\\*stat\\*\"\" will enable all events in\n           the sched subsystem except those that have \"stat\" in their names.\n     *-B*: Deletes the specified ftrace instance. There must be no\n           configuration options related to this instance in the command line.\n           For example: \"-v -B bar -B foo\" will delete instance bar and create\n           a new instance foo.\n    Note: the *-v* option was taken from the way grep(1) inverts the following\n    matches.\n--\n*-P* 'pid'::\n    This will filter only the specified process IDs. Using *-P* will let you\n    trace only events that are caused by the process.\n\n*-c*::\n     Used *-P* to trace the process' children too (if kernel supports it).\n\n*--user*::\n     Execute the specified *command* as given user.\n\n*-C* 'clock'::\n     Set the trace clock to \"clock\".\n\n     Use trace-cmd(1) list -C to see what clocks are available.\n\n*-l* 'function-name'::\n    This will limit the 'function' and 'function_graph' tracers to only trace\n    the given function name. More than one *-l* may be specified on the\n    command line to trace more than one function. The limited use of glob\n    expressions are also allowed. These are 'match\\*' to only filter functions\n    that start with 'match'. '\\*match' to only filter functions that end with\n    'match'. '\\*match\\*' to only filter on functions that contain 'match'.\n\n*-g* 'function-name'::\n    This option is for the function_graph plugin. It will graph the given\n    function. That is, it will only trace the function and all functions that\n    it calls. You can have more than one *-g* on the command line.\n\n*-n* 'function-name'::\n    This has the opposite effect of *-l*. The function given with the *-n*\n    option will not be traced. This takes precedence, that is, if you include\n    the same function for both *-n* and *-l*, it will not be traced.\n\n*-d*::\n    Some tracer plugins enable the function tracer by default. Like the\n    latency tracers. This option prevents the function tracer from being\n    enabled at start up.\n\n*-D*::\n    The option *-d* will try to use the function-trace option to disable the\n    function tracer (if available), otherwise it defaults to the proc file:\n    /proc/sys/kernel/ftrace_enabled, but will not touch it if the function-trace\n    option is available.  The *-D* option will disable both the ftrace_enabled\n    proc file as well as the function-trace option if it exists.\n\n    Note, this disable function tracing for all users, which includes users\n    outside of ftrace tracers (stack_tracer, perf, etc).\n\n*-O* 'option'::\n    Ftrace has various options that can be enabled or disabled. This allows\n    you to set them. Appending the text 'no' to an option disables it.\n    For example: \"-O nograph-time\" will disable the \"graph-time\" Ftrace\n    option.\n\n*-b* 'size'::\n    This sets the ring buffer size to 'size' kilobytes. Because the Ftrace\n    ring buffer is per CPU, this size is the size of each per CPU ring buffer\n    inside the kernel. Using \"-b 10000\" on a machine with 4 CPUs will make\n    Ftrace have a total buffer size of 40 Megs.\n\n*-B* 'buffer-name'::\n    If the kernel supports multiple buffers, this will add a buffer with\n    the given name. If the buffer name already exists, that buffer is just\n    reset.\n\n    After a buffer name is stated, all events added after that will be\n    associated with that buffer. If no buffer is specified, or an event\n    is specified before a buffer name, it will be associated with the\n    main (toplevel) buffer.\n\n     trace-cmd set -e sched -B block -e block -B time -e timer sleep 1\n\n    The above is will enable all sched events in the main buffer. It will\n    then create a 'block' buffer instance and enable all block events within\n    that buffer. A 'time' buffer instance is created and all timer events\n    will be enabled for that event.\n\n*-m* 'size'::\n    The max size in kilobytes that a per cpu buffer should be. Note, due\n    to rounding to page size, the number may not be totally correct.\n    Also, this is performed by switching between two buffers that are half\n    the given size thus the output may not be of the given size even if\n    much more was written.\n\n    Use this to prevent running out of diskspace for long runs.\n\n*-M* 'cpumask'::\n    Set the cpumask for to trace. It only affects the last buffer instance\n    given. If supplied before any buffer instance, then it affects the\n    main buffer. The value supplied must be a hex number.\n\n     trace-cmd set -p function -M c -B events13 -e all -M 5\n\n    If the -M is left out, then the mask stays the same. To enable all\n    CPUs, pass in a value of '-1'.\n\n*-i*::\n    By default, if an event is listed that trace-cmd does not find, it\n    will exit with an error. This option will just ignore events that are\n    listed on the command line but are not found on the system.\n\n*-q* | *--quiet*::\n    Suppresses normal output, except for errors.\n\n*--max-graph-depth* 'depth'::\n    Set the maximum depth the function_graph tracer will trace into a function.\n    A value of one will only show where userspace enters the kernel but not any\n    functions called in the kernel. The default is zero, which means no limit.\n\n*--cmdlines-size* 'size'::\n    Set the number of entries the kernel tracing file \"saved_cmdlines\" can\n    contain. This file is a circular buffer which stores the mapping between\n    cmdlines and PIDs. If full, it leads to unresolved cmdlines (\"<...>\") within\n    the trace. The kernel default value is 128.\n\n*--module* 'module'::\n    Filter a module's name in function tracing. It is equivalent to adding\n    ':mod:module' after all other functions being filtered. If no other function\n    filter is listed, then all modules functions will be filtered in the filter.\n\n    '--module snd'  is equivalent to  '-l :mod:snd'\n\n    '--module snd -l \"*jack*\"' is equivalent to '-l \"*jack*:mod:snd\"'\n\n    '--module snd -n \"*\"' is equivalent to '-n :mod:snd'\n\n*--stderr*::\n    Have output go to stderr instead of stdout, but the output of the command\n    executed will not be changed. This is useful if you want to monitor the\n    output of the command being executed, but not see the output from trace-cmd.\n\n*--fork*::\n    If a command is listed, then trace-cmd will wait for that command to finish,\n    unless the *--fork* option is specified. Then it will fork the command and\n    return immediately.\n\n*--verbose*[='level']::\n     Set the log level. Supported log levels are \"none\", \"critical\", \"error\", \"warning\",\n     \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting the log\n     level to specific value enables all logs from that and all previous levels.\n     The level will default to \"info\" if one is not specified.\n\n     Example: enable all critical, error and warning logs\n\n      trace-cmd set --verbose=warning\n\nEXAMPLES\n--------\n\nEnable all events for tracing:\n\n[source,shell]\n----\n # trace-cmd set -e all\n----\n\nSet the function tracer:\n\n[source,shell]\n----\n # trace-cmd set -p function\n----\n\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1), trace-cmd-profile(1)\n\nAUTHOR\n------\nWritten by Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-show.1.txt",
    "content": "TRACE-CMD-SHOW(1)\n=================\n\nNAME\n----\ntrace-cmd-show - show the contents of the Ftrace Linux kernel tracing buffer.\n\nSYNOPSIS\n--------\n*trace-cmd show* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) show displays the contents of one of the Ftrace Linux\nkernel tracing files: trace, snapshot, or trace_pipe. It is basically\nthe equivalent of doing:\n\n  cat /sys/kernel/debug/tracing/trace\n\nOPTIONS\n-------\n*-p*::\n    Instead of displaying the contents of the \"trace\" file, use the\n    \"trace_pipe\" file. The difference between the two is that the \"trace\"\n    file is static. That is, if tracing is stopped, the \"trace\" file\n    will show the same contents each time.\n\n    The \"trace_pipe\" file is a consuming read, where a read of the file\n    will consume the output of what was read and it will not read the\n    same thing a second time even if tracing is stopped. This file\n    als will block. If no data is available, trace-cmd show will stop\n    and wait for data to appear.\n\n*-s*::\n    Instead of reading the \"trace\" file, read the snapshot file. The snapshot\n    is made by an application writing into it and the kernel will perform\n    as swap between the currently active buffer and the current snapshot\n    buffer. If no more swaps are made, the snapshot will remain static.\n    This is not a consuming read.\n\n*-c* 'cpu'::\n    Read only the trace file for a specified CPU.\n\n*-f*::\n    Display the full path name of the file that is being displayed.\n\n*-B* 'buf'::\n    If a buffer instance was created, then the *-B* option will access the\n    files associated with the given buffer.\n\n*--tracing_on*::\n    Show if tracing is on for the given instance.\n\n*--current_tracer*::\n    Show what the current tracer is.\n\n*--buffer_size*::\n    Show the current buffer size (per-cpu)\n\n*--buffer_total_size*::\n     Show the total size of all buffers.\n\n*--buffer_subbuf_size*::\n     Show the size in kilobytes of the sub-buffers of the ring buffer.\n     The ring buffer is broken up into equal size sub-buffers were an event can only\n     be as big as the sub-buffer data section (the size minus its meta data).\n\n*--buffer_percent*::\n    Show the percentage the buffer must be filled before a reader that is blocked\n    on the trace_pipe_raw file will be woken up.\n\n  0       : wake up immediately on any new data\n  1 - 99  : wake up on this percentage of the sub-buffers being full\n  100     : wake up after the buffer is full and the writer is on the last sub-buffer\n\n*--ftrace_filter*::\n     Show what function filters are set.\n\n*--ftrace_notrace*::\n     Show what function disabled filters are set.\n\n*--ftrace_pid*::\n     Show the PIDs the function tracer is limited to (if any).\n\n*--graph_function*::\n     Show the functions that will be graphed.\n\n*--graph_notrace*::\n     Show the functions that will not be graphed.\n\n*--hist* '[system:]event'::\n     Show the content of a histogram \"hist\" file for a given event\n\n*--trigger* '[system:]event'::\n     Show the content of the \"trigger\" file for a given event\n\n*--cpumask*::\n     Show the mask of CPUs that tracing will trace.\n\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-snapshot.1.txt",
    "content": "TRACE-CMD-SNAPSHOT(1)\n=====================\n\nNAME\n----\ntrace-cmd-snapshot - take, reset, free, or show a Ftrace kernel snapshot\n\nSYNOPSIS\n--------\n*trace-cmd snapshot* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) snapshot controls or displays the Ftrace Linux kernel\nsnapshot feature (if the kernel supports it). This is useful to \"freeze\"\nan instance of a live trace but without stopping the trace.\n\n  trace-cmd start -p function\n  trace-cmd snapshot -s\n  trace-cmd snapshot\n [ dumps the content of buffer at 'trace-cmd snapshot -s' ]\n  trace-cmd snapshot -s\n  trace-cmd snapshot\n [ dumps the new content of the buffer at the last -s operation ]\n\nOPTIONS\n-------\n*-s*::\n    Take a snapshot of the currently running buffer.\n\n*-r*::\n    Clear out the buffer.\n\n*-f*::\n    Free the snapshot buffer. The buffer takes up memory inside the\n    kernel. It is best to free it when not in use. The first -s\n    operation will allocate it if it is not already allocated.\n\n*-c* 'cpu'::\n    Operate on a per cpu snapshot (may not be fully supported by all kernels)\n\n*-B* 'buf'::\n    If a buffer instance was created, then the *-B* option will operate on\n    the snapshot within the buffer.\n    \n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-split.1.txt",
    "content": "TRACE-CMD-SPLIT(1)\n==================\n\nNAME\n----\ntrace-cmd-split - split a trace.dat file into smaller files\n\nSYNOPSIS\n--------\n*trace-cmd split* ['OPTIONS'] ['start-time' ['end-time']]\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) split is used to break up a trace.dat into small files.\nThe 'start-time' specifies where the new file will start at. Using\n'trace-cmd-report(1)' and copying the time stamp given at a particular event,\ncan be used as input for either 'start-time' or 'end-time'. The split will\nstop creating files when it reaches an event after 'end-time'. If only the\nend-time is needed, use 0.0 as the start-time.\n\nIf start-time is left out, then the split will start at the beginning of the\nfile. If end-time is left out, then split will continue to the end unless it\nmeets one of the requirements specified by the options.\n\nOPTIONS\n-------\n*-i* 'file'::\n    If this option is not specified, then the split command will look for the\n    file named 'trace.dat'. This options will allow the reading of another\n    file other than 'trace.dat'.\n\n*-o* 'file'::\n    By default, the split command will use the input file name as a basis of\n    where to write the split files. The output file will be the input file\n    with an attached \\'.#\\' to the end: trace.dat.1, trace.dat.2, etc.\n\n    This option will change the name of the base file used.\n\n    -o file  will create file.1, file.2, etc.\n\n*-s* 'seconds'::\n    This specifies how many seconds should be recorded before the new file\n    should stop.\n\n*-m* 'milliseconds'::\n    This specifies how many milliseconds should be recorded before the new\n    file should stop.\n\n*-u* 'microseconds'::\n    This specifies how many microseconds should be recorded before the new\n    file should stop.\n\n*-e* 'events'::\n    This specifies how many events should be recorded before the new file\n    should stop.\n\n*-p* 'pages'::\n    This specifies the number of pages that should be recorded before the new\n    file should stop.\n\n   Note: only one of *-p*, *-e*, *-u*, *-m*, *-s* may be specified at a time.\n\n   If *-p* is specified, then *-c* is automatically set.\n\n*-r*::\n    This option causes the break up to repeat until end-time is reached (or\n    end of the input if end-time is not specified).\n\n    trace-cmd split -r -e 10000\n\n    This will break up trace.dat into several smaller files, each with at most\n    10,000 events in it.\n\n*-c*::\n    This option causes the above break up to be per CPU.\n\n    trace-cmd split -c -p 10\n\n    This will create a file that has 10 pages per each CPU from the input.\n\n*-C* 'cpu'::\n    This option will split for a single CPU. Only the cpu named will be extracted\n    from the file.\n\n    trace-cmd split -C 1\n\n    This will split out all the events for cpu 1 in the file.\n\n*--top*::\n    This allows to keep the top buffer.\n    The top buffer can be renamed using the '-b' option.\n\n    trace-cmd split --top\n\n    This will keep only the top buffer.\n\n    trace-cmd split --top -b old_top\n\n    This will keep only the top buffer and rename it 'old_top'.\n\n*-B* 'buffer'::\n    This allows to keep the selected buffer.\n    A buffer can be promoted to the top buffer using the '-t' option.\n\n    trace-cmd split -B timer -B sched\n\n    This will keep the 'timer' and 'sched' buffers.\n\n    trace-cmd split -B timer -t -B sched\n\n    This will keep the 'timer' and 'sched' buffers, with the events\n    from the 'timer' buffer promoted to the top instance.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-sqlhist.1.txt",
    "content": "TRACE-CMD-SQLHIST(1)\n====================\n\nNAME\n----\ntrace-cmd-sqlhist - Use SQL language to create / show creation of tracefs histograms and synthetic events\n\nSYNOPSIS\n--------\n*trace-cmd sqlhist* ['OPTIONS'] ['SQL-select-command']\n\nDESCRIPTION\n-----------\nThe trace-cmd sqlhist(1) will take an SQL like statement to create tracefs histograms and\nsynthetic events that can perform various actions for various handling of the\ndata.\n\nThe tracefs file system interfaces with the Linux tracing infrastructure that\nhas various dynamic and static events through out the kernel. Each of these\nevents can have a \"histogram\" attached to it, where the fields of the event\nwill define the buckets of the histogram.\n\nA synthetic event is a way to attach two separate events and use the fields\nand time stamps of those events to create a new dynamic event. This new\ndynamic event is call a synthetic event. The fields of each event can have\nsimple calculations done on them where, for example, the delta between\na field of one event to a field of the other event can be taken. This also\nworks for the time stamps of the events where the time delta between the\ntwo events can also be extracted and placed into the synthetic event.\n\nOther actions can be done from the fields of the events. A snapshot can\nbe taken of the kernel ring buffer a variable used in the synthetic\nevent creating hits a max, or simply changes.\n\nThe commands to create histograms and synthetic events are complex and\nnot easy to remember. *trace-cmd sqlhist* is used to convert SQL syntax into the\ncommands needed to create the histogram or synthetic event.\n\nThe *SQL-select-command* is a SQL string defined by *tracefs_sqlhist*(3).\n\nNote, this must be run as root (or sudo) as interacting with the tracefs\ndirectory requires root privilege, unless the *-t* option is given with\na copy of the _tracefs_ directory and its events.\n\nOPTIONS\n-------\n*-n* 'name'::\n    The name of the synthetic event to create. This event can then be\n    used like any other event, and enabled via *trace-cmd record*(1).\n\n*-t* 'tracefs-dir'::\n    In order to test this out as non root user, a copy of the tracefs directory\n    can be used, and passing that directory with this option will allow\n    the program to work. Obviously, *-e* will not work as non-root because\n    it will not be able to execute.\n\n    # mkdir /tmp/tracing\n    # cp -r /sys/kernel/tracing/events /tmp/tracing\n    # exit\n    $ trace-cmd sqlhist -t /tmp/tracing ...\n\n*-e*::\n    Not only display the commands to create the histogram, but also execute them.\n    This requires root privilege.\n\n*-f* 'file'::\n    Instead of reading the SQL commands from the command line, read them from\n    _file_. If _file_ is '-' then read from standard input.\n\n*-m* 'var'::\n    Do the given action when the variable _var_ hits a new maximum. This can\n    not be used with *-c*. The _var_ must be defined in the *SQL-select-command*.\n\n*-c* 'var'::\n    Do the given action when the variable _var_ changes its value. This can\n    not be used with *-m*. The _var_ must be defined in the *SQL-select-command*.\n\n*-s*::\n    Perform a snapshot instead of calling the synthetic event.\n\n*-T*::\n    Perform both a snapshot and trace the synthetic event.\n\n*-S* 'fields[,fields]'::\n    Save the given fields. The fields must be fields of the \"end\" event given\n    in the *SQL-select-command*\n\n*-B* 'instance'::\n    For simple statements that only produce a histogram, the instance given here\n    will be where the histogram will be created. This is ignored for full synthetic\n    event creation, as sythetic events have a global affect on all tracing instances,\n    where as, histograms only affect a single instance.\n\nEXAMPLES\n--------\n\nAs described above, for testing purposes, make a copy of the event directory:\n[source, c]\n--\n   $ mkdir /tmp/tracing\n   $ sudo cp -r /sys/kernel/tracing/events /tmp/tracing/\n   $ sudo chmod -R 0644 /tmp/tracing/\n--\n\nFor an example of simple histogram output using the copy of the tracefs directory.\n[source, c]\n--\n   $ trace-cmd sqlhist -t /tmp/tracing/ 'SELECT CAST(call_site as SYM-OFFSET), bytes_req, CAST(bytes_alloc AS _COUNTER_) FROM kmalloc'\n--\n\nProduces the output:\n[source, c]\n--\n   echo 'hist:keys=call_site.sym-offset,bytes_req:vals=bytes_alloc' > /sys/kernel/tracing/events/kmem/kmalloc/trigger\n--\n\nWhich could be used by root:\n[source, c]\n--\n   # echo 'hist:keys=call_site.sym-offset,bytes_req:vals=bytes_alloc' > /sys/kernel/tracing/events/kmem/kmalloc/trigger\n   # cat /sys/kernel/tracing/events/kmem/kmalloc/hist\n# event histogram\n#\n# trigger info: hist:keys=call_site.sym-offset,bytes_req:vals=hitcount,bytes_alloc:sort=hitcount:size=2048 [active]\n#\n\n{ call_site: [ffffffff813f8d8a] load_elf_phdrs+0x4a/0xb0                               , bytes_req:        728 } hitcount:          1  bytes_alloc:       1024\n{ call_site: [ffffffffc0c69e74] nf_ct_ext_add+0xd4/0x1d0 [nf_conntrack]                , bytes_req:        128 } hitcount:          1  bytes_alloc:        128\n{ call_site: [ffffffff818355e6] dma_resv_get_fences+0xf6/0x440                         , bytes_req:          8 } hitcount:          1  bytes_alloc:          8\n{ call_site: [ffffffffc06dc73f] intel_gt_get_buffer_pool+0x15f/0x290 [i915]            , bytes_req:        424 } hitcount:          1  bytes_alloc:        512\n{ call_site: [ffffffff813f8d8a] load_elf_phdrs+0x4a/0xb0                               , bytes_req:        616 } hitcount:          1  bytes_alloc:       1024\n{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:         32 } hitcount:          1  bytes_alloc:         32\n{ call_site: [ffffffffc070749d] shmem_get_pages+0xad/0x5d0 [i915]                      , bytes_req:         16 } hitcount:          1  bytes_alloc:         16\n{ call_site: [ffffffffc07507f5] intel_framebuffer_create+0x25/0x60 [i915]              , bytes_req:        408 } hitcount:          1  bytes_alloc:        512\n{ call_site: [ffffffffc06fc20f] eb_parse+0x34f/0x910 [i915]                            , bytes_req:        408 } hitcount:          1  bytes_alloc:        512\n{ call_site: [ffffffffc0700ebd] i915_gem_object_get_pages_internal+0x5d/0x270 [i915]   , bytes_req:         16 } hitcount:          1  bytes_alloc:         16\n{ call_site: [ffffffffc0771188] intel_frontbuffer_get+0x38/0x220 [i915]                , bytes_req:        400 } hitcount:          1  bytes_alloc:        512\n{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:        128 } hitcount:          1  bytes_alloc:        128\n{ call_site: [ffffffff813f8f45] load_elf_binary+0x155/0x1680                           , bytes_req:         28 } hitcount:          1  bytes_alloc:         32\n{ call_site: [ffffffffc07038c8] __assign_mmap_offset+0x208/0x3d0 [i915]                , bytes_req:        288 } hitcount:          1  bytes_alloc:        512\n{ call_site: [ffffffff813737b2] alloc_bprm+0x32/0x2f0                                  , bytes_req:        416 } hitcount:          1  bytes_alloc:        512\n{ call_site: [ffffffff813f9027] load_elf_binary+0x237/0x1680                           , bytes_req:         64 } hitcount:          1  bytes_alloc:         64\n{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:         64 } hitcount:          1  bytes_alloc:         64\n{ call_site: [ffffffffc040ffe7] drm_vma_node_allow+0x27/0xe0 [drm]                     , bytes_req:         40 } hitcount:          2  bytes_alloc:        128\n{ call_site: [ffffffff813cda98] __do_sys_timerfd_create+0x58/0x1c0                     , bytes_req:        336 } hitcount:          2  bytes_alloc:       1024\n{ call_site: [ffffffff818355e6] dma_resv_get_fences+0xf6/0x440                         , bytes_req:         40 } hitcount:          2  bytes_alloc:        128\n{ call_site: [ffffffff8139b75a] single_open+0x2a/0xa0                                  , bytes_req:         32 } hitcount:          2  bytes_alloc:         64\n{ call_site: [ffffffff815df715] bio_kmalloc+0x25/0x80                                  , bytes_req:        136 } hitcount:          2  bytes_alloc:        384\n{ call_site: [ffffffffc071e5cd] i915_vma_work+0x1d/0x50 [i915]                         , bytes_req:        416 } hitcount:          3  bytes_alloc:       1536\n{ call_site: [ffffffff81390d0d] alloc_fdtable+0x4d/0x100                               , bytes_req:         56 } hitcount:          3  bytes_alloc:        192\n{ call_site: [ffffffffc06ff65f] i915_gem_do_execbuffer+0x158f/0x2440 [i915]            , bytes_req:         16 } hitcount:          4  bytes_alloc:         64\n{ call_site: [ffffffff8137713c] alloc_pipe_info+0x5c/0x230                             , bytes_req:        384 } hitcount:          5  bytes_alloc:       2560\n{ call_site: [ffffffff813771b4] alloc_pipe_info+0xd4/0x230                             , bytes_req:        640 } hitcount:          5  bytes_alloc:       5120\n{ call_site: [ffffffff81834cdb] dma_resv_list_alloc+0x1b/0x40                          , bytes_req:         40 } hitcount:          6  bytes_alloc:        384\n{ call_site: [ffffffff81834cdb] dma_resv_list_alloc+0x1b/0x40                          , bytes_req:         56 } hitcount:          9  bytes_alloc:        576\n{ call_site: [ffffffff8120086e] tracing_map_sort_entries+0x9e/0x3e0                    , bytes_req:         24 } hitcount:         60  bytes_alloc:       1920\n\nTotals:\n    Hits: 122\n    Entries: 30\n    Dropped: 0\n--\n\nNote, although the examples use uppercase for the SQL keywords, they do not have\nto be. 'SELECT' could also be 'select' or even 'sElEcT'.\n\nBy using the full SQL language, synthetic events can be made and processed.\nFor example, using *trace-cmd sqlhist* along with *trace-cmd record*(1), wake up latency can\nbe recorded by creating a synthetic event by attaching the _sched_waking_\nand the _sched_switch_ events.\n\n[source, c]\n--\n  # trace-cmd sqlhist -n wakeup_lat -e -T -m lat 'SELECT end.next_comm AS comm, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat FROM ' \\\n    'sched_waking AS start JOIN sched_switch AS end ON start.pid = end.next_pid WHERE end.next_prio < 100 && end.next_comm == \"cyclictest\"'\n  # trace-cmd start -e all -e wakeup_lat -R stacktrace\n  # cyclictest -l 1000 -p80 -i250  -a -t -q -m -d 0 -b 1000 --tracemark\n  # trace-cmd show -s | tail -30\n          <idle>-0       [002] dNh4 23454.902246: sched_wakeup: comm=cyclictest pid=12272 prio=120 target_cpu=002\n          <idle>-0       [005] ...1 23454.902246: cpu_idle: state=4294967295 cpu_id=5\n          <idle>-0       [007] d..1 23454.902246: cpu_idle: state=0 cpu_id=7\n          <idle>-0       [002] dNh1 23454.902247: hrtimer_expire_exit: hrtimer=0000000037956dc2\n          <idle>-0       [005] d..1 23454.902248: cpu_idle: state=0 cpu_id=5\n          <idle>-0       [002] dNh1 23454.902248: write_msr: 6e0, value 4866ce957272\n          <idle>-0       [006] ...1 23454.902248: cpu_idle: state=4294967295 cpu_id=6\n          <idle>-0       [002] dNh1 23454.902249: local_timer_exit: vector=236\n          <idle>-0       [006] d..1 23454.902250: cpu_idle: state=0 cpu_id=6\n          <idle>-0       [002] .N.1 23454.902250: cpu_idle: state=4294967295 cpu_id=2\n          <idle>-0       [002] dN.1 23454.902251: rcu_utilization: Start context switch\n          <idle>-0       [002] dN.1 23454.902252: rcu_utilization: End context switch\n          <idle>-0       [001] ...1 23454.902252: cpu_idle: state=4294967295 cpu_id=1\n          <idle>-0       [002] dN.3 23454.902253: prandom_u32: ret=3692516021\n          <idle>-0       [001] d..1 23454.902254: cpu_idle: state=0 cpu_id=1\n          <idle>-0       [002] d..2 23454.902254: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=cyclictest next_pid=12275 next_prio=19\n          <idle>-0       [002] d..4 23454.902256: wakeup_lat: next_comm=cyclictest lat=17\n          <idle>-0       [002] d..5 23454.902258: <stack trace>\n => trace_event_raw_event_synth\n => action_trace\n => event_hist_trigger\n => event_triggers_call\n => trace_event_buffer_commit\n => trace_event_raw_event_sched_switch\n => __traceiter_sched_switch\n => __schedule\n => schedule_idle\n => do_idle\n => cpu_startup_entry\n => secondary_startup_64_no_verify\n--\n\nHere's the options for above example explained:\n\n *-n wakeup_lat* ::\n     Name the synthetic event to use *wakeup_lat*.\n\n *-e*::\n     Execute the commands that are printed.\n\n *-T*::\n     Perform both a trace action and then a snapshot action (swap the buffer into the static 'snapshot' buffer).\n\n *-m lat*::\n     Trigger the actions whenever 'lat' hits a new maximum value.\n\nNow a breakdown of the SQL statement:\n[source, c]\n--\n 'SELECT end.next_comm AS comm, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat FROM ' \\\n    'sched_waking AS start JOIN sched_switch AS end ON start.pid = end.next_pid WHERE end.next_prio < 100 && end.next_comm == \"cyclictest\"'\n--\n *end.next_comm AS comm*::\n   Save the 'sched_switch' field *next_comm* and place it into the *comm* field of the 'wakeup_lat' synthetic event.\n\n *(end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat*::\n   Take the delta of the time stamps from the 'sched_switch' event and the 'sched_waking' event.\n   As time stamps are usually recorded in nanoseconds, *TIMESTAMP* would give the full nanosecond time stamp,\n   but here, the *TIMESTAMP_USECS* will truncate it into microseconds. The value is saved in the\n   variable *lat*, which will also be recorded in the synthetic event.\n\n *FROM 'sched_waking' AS start JOIN sched_switch AS end ON start.pid = end.next_pid*::\n   Create the synthetic event by joining _sched_waking_ to _sched_switch_, matching\n   the _sched_waking_ 'pid' field with the _sched_switch_ 'next_pid' field.\n   Also make *start* an alias for _sched_waking_ and *end* an alias for _sched_switch_\n   which then an use *start* and *end* as a subsitute for _sched_waking_ and _sched_switch_\n   respectively through out the rest of the SQL statement.\n\n *WHERE end.next_prio < 100 && end.next_comm == \"cyclictest\"*::\n   Filter the logic where it executes only if _sched_waking_ 'next_prio' field\n   is less than 100. (Note, in the Kernel, priorities are inverse, and the real-time\n   priorities are represented from 0-100 where 0 is the highest priority).\n   Also only trace when the 'next_comm' (the task scheduling in) of the _sched_switch_\n   event has the name \"cyclictest\".\n\nFor the *trace-cmd*(3) command:\n[source, c]\n--\n   trace-cmd start -e all -e wakeup_lat -R stacktrace\n--\n\n *trace-cmd start*::\n   Enables tracing (does not record to a file).\n\n *-e all*::\n   Enable all events\n\n *-e wakeup_lat -R stacktrace*::\n   have the \"wakeup_lat\" event (our synthetic event) enable the *stacktrace* trigger, were\n   for every instance of the \"wakeup_lat\" event, a kernel stack trace will be recorded\n   in the ring buffer.\n\nAfter calling *cyclictest* (a real-time tool to measure wakeup latency), read the snapshot\nbuffer.\n\n *trace-cmd show -s*::\n   *trace-cmd show* reads the kernel ring buffer, and the *-s* option will read the *snapshot*\n   buffer instead of the normal one.\n\n[source, c]\n--\n <idle>-0       [002] d..4 23454.902256: wakeup_lat: next_comm=cyclictest lat=17\n--\n  We see on the \"wakeup_lat\" event happened on CPU 2, with a wake up latency 17 microseconds.\n\nThis can be extracted into a *trace.dat* file that *trace-cmd*(3) can read and do further\nanalysis, as well as *kernelshark*.\n\n[source, c]\n--\n    # trace-cmd extract -s\n    # trace-cmd report --cpu 2 | tail -30\n          <idle>-0     [002] 23454.902238: prandom_u32:          ret=1633425088\n          <idle>-0     [002] 23454.902239: sched_wakeup:         cyclictest:12275 [19] CPU:002\n          <idle>-0     [002] 23454.902241: hrtimer_expire_exit:  hrtimer=0xffffbbd68286fe60\n          <idle>-0     [002] 23454.902241: hrtimer_cancel:       hrtimer=0xffffbbd6826efe70\n          <idle>-0     [002] 23454.902242: hrtimer_expire_entry: hrtimer=0xffffbbd6826efe70 now=23455294430750 function=hrtimer_wakeup/0x0\n          <idle>-0     [002] 23454.902243: sched_waking:         comm=cyclictest pid=12272 prio=120 target_cpu=002\n          <idle>-0     [002] 23454.902244: prandom_u32:          ret=1102749734\n          <idle>-0     [002] 23454.902246: sched_wakeup:         cyclictest:12272 [120] CPU:002\n          <idle>-0     [002] 23454.902247: hrtimer_expire_exit:  hrtimer=0xffffbbd6826efe70\n          <idle>-0     [002] 23454.902248: write_msr:            6e0, value 4866ce957272\n          <idle>-0     [002] 23454.902249: local_timer_exit:     vector=236\n          <idle>-0     [002] 23454.902250: cpu_idle:             state=4294967295 cpu_id=2\n          <idle>-0     [002] 23454.902251: rcu_utilization:      Start context switch\n          <idle>-0     [002] 23454.902252: rcu_utilization:      End context switch\n          <idle>-0     [002] 23454.902253: prandom_u32:          ret=3692516021\n          <idle>-0     [002] 23454.902254: sched_switch:         swapper/2:0 [120] R ==> cyclictest:12275 [19]\n          <idle>-0     [002] 23454.902256: wakeup_lat:           next_comm=cyclictest lat=17\n          <idle>-0     [002] 23454.902258: kernel_stack:         <stack trace >\n=> trace_event_raw_event_synth (ffffffff8121a0db)\n=> action_trace (ffffffff8121e9fb)\n=> event_hist_trigger (ffffffff8121ca8d)\n=> event_triggers_call (ffffffff81216c72)\n=> trace_event_buffer_commit (ffffffff811f7618)\n=> trace_event_raw_event_sched_switch (ffffffff8110fda4)\n=> __traceiter_sched_switch (ffffffff8110d449)\n=> __schedule (ffffffff81c02002)\n=> schedule_idle (ffffffff81c02c86)\n=> do_idle (ffffffff8111e898)\n=> cpu_startup_entry (ffffffff8111eba9)\n=> secondary_startup_64_no_verify (ffffffff81000107)\n--\n\nSEE ALSO\n--------\ntrace-cmd(1), tracefs_sqlhist(3)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2021 , Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-stack.1.txt",
    "content": "TRACE-CMD-STACK(1)\n==================\n\nNAME\n----\ntrace-cmd-stack - read, enable or disable Ftrace Linux kernel stack tracing.\n\nSYNOPSIS\n--------\n*trace-cmd stack*\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) stack enables the Ftrace stack tracer within the kernel.\nThe stack tracer enables the function tracer and at each function call\nwithin the kernel, the stack is checked. When a new maximum usage stack\nis discovered, it is recorded.\n\nWhen no option is used, the current stack is displayed.\n\nTo enable the stack tracer, use the option *--start*, and to disable\nthe stack tracer, use the option *--stop*. The output will be the maximum\nstack found since the start was enabled.\n\nUse *--reset* to reset the stack counter to zero.\n\nUser *--verbose*[='level'] to set the log level. Supported log levels are \"none\", \"critical\", \"error\",\n\"warning\", \"info\", \"debug\", \"all\" or their identifiers \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\". Setting\nthe log level to specific value enables all logs from that and all previous levels. The level will\ndefault to \"info\" if one is not specified.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-start.1.txt",
    "content": "TRACE-CMD-START(1)\n==================\n\nNAME\n----\ntrace-cmd-start - start the Ftrace Linux kernel tracer without recording\n\nSYNOPSIS\n--------\n*trace-cmd start* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) start enables all the Ftrace tracing the same way\ntrace-cmd-record(1) does. The difference is that it does not run threads to\ncreate a trace.dat file. This is useful just to enable Ftrace and you are only\ninterested in the trace after some event has occurred and the trace is\nstopped. Then the trace can be read straight from the Ftrace pseudo file\nsystem or can be extracted with trace-cmd-extract(1).\n\nOPTIONS\n-------\nThe options are the same as 'trace-cmd-record(1)', except that it does not\ntake options specific to recording (*-s*, *-o*, *-N*, and *-t*).\n\n*--fork* ::\n   This option is only available for trace-cmd start. It tells trace-cmd\n   to not wait for the process to finish before returning.\n   With this option, trace-cmd start will return right after it forks\n   the process on the command line. This option only has an effect if\n   trace-cmd start also executes a command.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-stop(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-stat.1.txt",
    "content": "TRACE-CMD-STAT(1)\n=================\n\nNAME\n----\ntrace-cmd-stat - show the status of the tracing (ftrace) system\n\nSYNOPSIS\n--------\n*trace-cmd stat* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) stat displays the various status of the tracing (ftrace)\nsystem. The status that it shows is:\n\n*Instances:* List all configured ftrace instances.\n\n*Tracer:* if one of the tracers (like function_graph) is active. Otherwise\n  nothing is displayed.\n\n*Events:* Lists the events that are enable.\n\n*Event filters:* Shows any filters that are set for any events\n\n*Function filters:* Shows any filters for the function tracers\n\n*Graph functions:* Shows any functions that the function graph tracer should graph\n\n*Buffers:* Shows the trace buffer size if they have been expanded.\n   By default, tracing buffers are in a compressed format until they are used.\n   If they are compressed, the buffer display will not be shown.\n\n*Trace clock:* If the tracing clock is anything other than the default \"local\"\n   it will be displayed.\n\n*Trace CPU mask:* If not all available CPUs are in the tracing CPU mask, then\n   the tracing CPU mask will be displayed.\n\n*Trace max latency:* Shows the value of the trace max latency if it is other than zero.\n\n*Kprobes:* Shows any kprobes that are defined for tracing.\n\n*Uprobes:* Shows any uprobes that are defined for tracing.\n\n*Error log:* Dump the content of ftrace error_log file.\n\nOPTIONS\n-------\n*-B* 'buffer-name'::\n    Display the status of a given buffer instance. May be specified more than once\n    to display the status of multiple instances.\n\n*-t*::\n    If *-B* is also specified, show the status of the top level tracing directory\n    as well as the instance(s).\n\n*-o*::\n    Display the all the options along with their values. If they start with \"no\", then\n    the option is disabled.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-split(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2014 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-stop.1.txt",
    "content": "TRACE-CMD-STOP(1)\n=================\n\nNAME\n----\ntrace-cmd-stop - stop the Ftrace Linux kernel tracer from writing to the ring\nbuffer.\n\nSYNOPSIS\n--------\n*trace-cmd stop* ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) stop is a complement to 'trace-cmd-start(1)'. This will\ndisable Ftrace from writing to the ring buffer. This does not stop the\noverhead that the tracing may incur. Only the updating of the ring buffer is\ndisabled, the Ftrace tracing may still be inducing overhead.\n\nAfter stopping the trace, the 'trace-cmd-extract(1)' may strip out the data\nfrom the ring buffer and create a trace.dat file. The Ftrace pseudo file\nsystem may also be examined.\n\nTo disable the tracing completely to remove the overhead it causes, use\n'trace-cmd-reset(1)'. But after a reset is performed, the data that has been\nrecorded is lost.\n\nOPTIONS\n-------\n*-B* 'buffer-name'::\n    If the kernel supports multiple buffers, this will stop the trace for\n    only the given buffer. It does not affect any other buffer. This may be\n    used multiple times to specify different buffers. When this option is\n    used, the top level instance will not be stopped unless *-t* is given.\n\n*-a*::\n    Stop the trace for all existing buffer instances. When this option\n    is used, the top level instance will not be stopped unless *-t* is given.\n\n*-t*::\n    Stops the top level instance buffer. Without the *-B* or *-a* option this\n    is the same as the default. But if *-B* or *-a* is used, this is\n    required if the top level instance buffer should also be stopped.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd-stream.1.txt",
    "content": "TRACE-CMD-STREAM(1)\n===================\n\nNAME\n----\ntrace-cmd-stream - stream a trace to stdout as it is happening\n\nSYNOPSIS\n--------\n*trace-cmd stream ['OPTIONS']* ['command']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) stream will start tracing just like trace-cmd-record(1), except\nit will not record to a file and instead it will read the binary buffer\nas it is happening, convert it to a human readable format and write it to\nstdout.\n\nThis is basically the same as trace-cmd-start(1) and then doing a trace-cmd-show(1)\nwith the *-p* option. trace-cmd-stream is not as efficient as reading from the\npipe file as most of the stream work is done in userspace. This is useful if\nit is needed to do the work mostly in userspace instead of the kernel, and stream\nalso helps to debug trace-cmd-profile(1) which uses the stream code to perform\nthe live data analysis for the profile.\n\n\nOPTIONS\n-------\n   These are the same as trace-cmd-record(1), except that it does not take\n   the *-o* option.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-reset(1), trace-cmd-split(1),\ntrace-cmd-list(1), trace-cmd-listen(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2014 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd.1.txt",
    "content": "TRACE-CMD(1)\n============\n\nNAME\n----\ntrace-cmd - interacts with Ftrace Linux kernel internal tracer\n\nSYNOPSIS\n--------\n*trace-cmd* 'COMMAND' ['OPTIONS']\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) command interacts with the Ftrace tracer that is built inside\nthe Linux kernel. It interfaces with the Ftrace specific files found in the\ndebugfs file system under the tracing directory. A 'COMMAND' must be\nspecified to tell trace-cmd what to do.\n\n\nCOMMANDS\n--------\n\n  record  - record a live trace and write a trace.dat file to the\n            local disk or to the network.\n\n  set     - set a ftrace configuration parameter.\n\n  report  - reads a trace.dat file and converts the binary data to a\n            ASCII text readable format.\n\n  stream  - Start tracing and read the output directly\n\n  profile - Start profiling and read the output directly\n\n  hist    - show a histogram of the events.\n\n  stat    - show tracing (ftrace) status of the running system\n\n  options - list the plugin options that are available to *report*\n\n  start   - start the tracing without recording to a trace.dat file.\n\n  stop    - stop tracing (only disables recording, overhead of tracer\n            is still in effect)\n\n  restart - restart tracing from a previous stop (only effects recording)\n\n  extract - extract the data from the kernel buffer and create a trace.dat\n            file.\n\n  show    - display the contents of one of the Ftrace Linux kernel tracing files\n\n  reset   - disables all tracing and gives back the system performance.\n            (clears all data from the kernel buffers)\n\n  clear   - clear the content of the Ftrace ring buffers.\n\n  split   - splits a trace.dat file into smaller files.\n\n  list    - list the available plugins or events that can be recorded.\n\n  listen  - open up a port to listen for remote tracing connections.\n\n  agent   - listen on a vsocket for trace clients\n\n  setup-guest - create FIFOs for tracing guest VMs\n\n  restore - restore the data files of a crashed run of trace-cmd record\n\n  snapshot- take snapshot of running trace\n\n  stack   - run and display the stack tracer\n\n  check-events - parse format strings for all trace events and return\n                 whether all formats are parseable\n\n  convert   - convert trace files\n\n  attach   - attach a host trace.dat file to a guest trace.dat file\n\n  dump    - read out the meta data from a trace file\n\nOPTIONS\n-------\n\n*-h*, --help::\n    Display the help text.\n\nOther options see the man page for the corresponding command.\n\nSEE ALSO\n--------\ntrace-cmd-record(1), trace-cmd-report(1), trace-cmd-hist(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-restore(1), trace-cmd-stack(1), trace-cmd-convert(1),\ntrace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1),\ntrace-cmd.dat(5), trace-cmd-check-events(1), trace-cmd-stat(1),\ntrace-cmd-attach(1)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd.dat.v6.5.txt",
    "content": "TRACE-CMD.DAT.v6(5)\n===================\n\nNAME\n----\ntrace-cmd.dat.v6 - trace-cmd version 6 file format\n\nSYNOPSIS\n--------\n*trace-cmd.dat* ignore\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) utility produces a \"trace.dat\" file. The file may also\nbe named anything depending if the user specifies a different output name,\nbut it must have a certain binary format. The file is used\nby trace-cmd to save kernel traces into it and be able to extract\nthe trace from it at a later point (see *trace-cmd-report(1)*).\n\n\nINITIAL FORMAT\n--------------\n\n  The first three bytes contain the magic value:\n\n     0x17 0x08  0x44\n\n  The next 7 bytes contain the characters:\n\n     \"tracing\"\n\n  The next set of characters contain a null '\\0' terminated string\n  that contains the version of the file:\n\n     \"6\\0\"\n\n  The next 1 byte contains the flags for the file endianess:\n\n     0 = little endian\n     1 = big endian\n\n  The next byte contains the number of bytes per \"long\" value:\n\n     4 - 32-bit long values\n     8 - 64-bit long values\n\n  Note: This is the long size of the target's userspace. Not the\n  kernel space size.\n\n  [ Now all numbers are written in file defined endianess. ]\n\n  The next 4 bytes are a 32-bit word that defines what the traced\n  host machine page size was.\n\nHEADER INFO FORMAT\n------------------\n\n  Directly after the initial format comes information about the\n  trace headers recorded from the target box.\n\n  The next 12 bytes contain the string:\n\n    \"header_page\\0\"\n\n  The next 8 bytes are a 64-bit word containing the size of the\n  page header information stored next.\n\n  The next set of data is of the size read from the previous 8 bytes,\n  and contains the data retrieved from debugfs/tracing/events/header_page.\n\n  Note: The size of the second field \\fBcommit\\fR contains the target\n  kernel long size. For example:\n\n  field: local_t commit;\toffset:8;\t\\fBsize:8;\\fR\tsigned:1;\n\n  shows the kernel has a 64-bit long.\n\n  The next 13 bytes contain the string:\n\n  \"header_event\\0\"\n\n  The next 8 bytes are a 64-bit word containing the size of the\n  event header information stored next.\n\n  The next set of data is of the size read from the previous 8 bytes\n  and contains the data retrieved from debugfs/tracing/events/header_event.\n\n  This data allows the trace-cmd tool to know if the ring buffer format\n  of the kernel made any changes.\n\nFTRACE EVENT FORMATS\n--------------------\n\n  Directly after the header information comes the information about\n  the Ftrace specific events. These are the events used by the Ftrace plugins\n  and are not enabled by the event tracing.\n\n  The next 4 bytes contain a 32-bit word of the number of Ftrace event\n  format files that are stored in the file.\n\n  For the number of times defined by the previous 4 bytes is the\n  following:\n\n  8 bytes for the size of the Ftrace event format file.\n\n  The Ftrace event format file copied from the target machine:\n  debugfs/tracing/events/ftrace/<event>/format\n\nEVENT FORMATS\n-------------\n\n  Directly after the Ftrace formats comes the information about\n  the event layout.\n\n  The next 4 bytes are a 32-bit word containing the number of\n  event systems that are stored in the file. These are the\n  directories in debugfs/tracing/events excluding the \\fBftrace\\fR\n  directory.\n\n  For the number of times defined by the previous 4 bytes is the\n  following:\n\n  A null-terminated string containing the system name.\n\n  4 bytes containing a 32-bit word containing the number\n  of events within the system.\n\n  For the number of times defined in the previous 4 bytes is the\n  following:\n\n  8 bytes for the size of the event format file.\n\n  The event format file copied from the target machine:\n  debugfs/tracing/events/<system>/<event>/format\n\nKALLSYMS INFORMATION\n--------------------\n\n  Directly after the event formats comes the information of the mapping\n  of function addresses to the function names.\n\n  The next 4 bytes are a 32-bit word containing the size of the\n  data holding the function mappings.\n\n  The next set of data is of the size defined by the previous 4 bytes\n  and contains the information from the target machine's file:\n  /proc/kallsyms\n\n\nTRACE_PRINTK INFORMATION\n------------------------\n\n  If a developer used trace_printk() within the kernel, it may\n  store the format string outside the ring buffer.\n  This information can be found in:\n  debugfs/tracing/printk_formats\n\n  The next 4 bytes are a 32-bit word containing the size of the\n  data holding the printk formats.\n\n  The next set of data is of the size defined by the previous 4 bytes\n  and contains the information from debugfs/tracing/printk_formats.\n\n\nPROCESS INFORMATION\n-------------------\n\n  Directly after the trace_printk formats comes the information mapping\n  a PID to a process name.\n\n  The next 8 bytes contain a 64-bit word that holds the size of the\n  data mapping the PID to a process name.\n\n  The next set of data is of the size defined by the previous 8 bytes\n  and contains the information from debugfs/tracing/saved_cmdlines.\n\n\nREST OF TRACE-CMD HEADER\n------------------------\n\n  Directly after the process information comes the last bit of the\n  trace.dat file header.\n\n  The next 4 bytes are a 32-bit word defining the number of CPUs that\n  were discovered on the target machine (and has matching trace data\n  for it).\n\n  The next 10 bytes are one of the following:\n\n    \"options  \\0\"\n\n    \"latency  \\0\"\n\n    \"flyrecord\\0\"\n\n  If it is \"options  \\0\" then:\n\n  The next 2 bytes are a 16-bit word defining the current option.\n  If the the value is zero then there are no more options.\n\n  Otherwise, the next 4 bytes contain a 32-bit word containing the\n  option size. If the reader does not know how to handle the option\n  it can simply skip it. Currently there are no options defined,\n  but this is here to extend the data.\n\n  The next option will be directly after the previous option, and\n  the options ends with a zero in the option type field.\n\n  The next 10 bytes after the options are one of the following:\n\n  \"latency  \\0\"\n\n  \"flyrecord\\0\"\n\n  which would follow the same as if options were not present.\n\n  If the value is \"latency  \\0\", then the rest of the file is\n  simply ASCII text that was taken from the target's:\n  debugfs/tracing/trace\n\n  If the value is \"flyrecord\\0\", the following is present:\n\n  For the number of CPUs that were read earlier, the\n  following is present:\n\n  8 bytes that are a 64-bit word containing the offset into the file\n  that holds the data for the CPU.\n\n  8 bytes that are a 64-bit word containing the size of the CPU\n  data at that offset.\n\nCPU DATA\n--------\n\n  The CPU data is located in the part of the file that is specified\n  in the end of the header. Padding is placed between the header and\n  the CPU data, placing the CPU data at a page aligned (target page) position\n  in the file.\n\n  This data is copied directly from the Ftrace ring buffer and is of the\n  same format as the ring buffer specified by the event header files\n  loaded in the header format file.\n\n  The trace-cmd tool will try to \\fBmmap(2)\\fR the data page by page with the\n  target's page size if possible. If it fails to mmap, it will just read the\n  data instead.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1),\ntrace-cmd.dat(5)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "Documentation/trace-cmd/trace-cmd.dat.v7.5.txt",
    "content": "TRACE-CMD.DAT.v7(5)\n===================\n\nNAME\n----\ntrace-cmd.dat.v7 - trace-cmd version 7 file format\n\nSYNOPSIS\n--------\n*trace-cmd.dat* ignore\n\nDESCRIPTION\n-----------\nThe trace-cmd(1) utility produces a \"trace.dat\" file. The file may also\nbe named anything depending if the user specifies a different output name,\nbut it must have a certain binary format. The file is used\nby trace-cmd to save kernel traces into it and be able to extract\nthe trace from it at a later point (see *trace-cmd-report(1)*).\n\n\nINITIAL FORMAT\n--------------\n\n  The first three bytes contain the magic value:\n\n     0x17 0x08  0x44\n\n  The next 7 bytes contain the characters:\n\n     \"tracing\"\n\n  The next set of characters contain a null '\\0' terminated string\n  that contains the version of the file:\n\n     \"7\\0\"\n\n  The next 1 byte contains the flags for the file endianess:\n\n     0 = little endian\n     1 = big endian\n\n  The next byte contains the number of bytes per \"long\" value:\n\n     4 - 32-bit long values\n     8 - 64-bit long values\n\n  Note: This is the long size of the target's user space. Not the\n  kernel space size.\n\n  [ Now all numbers are written in file defined endianess. ]\n\n  The next 4 bytes are a 32-bit word that defines what the traced\n  host machine page size was.\n\n  The compression algorithm header is written next:\n     \"name\\0version\\0\"\n  where \"name\" and \"version\" are strings, name and version of the\n  compression algorithm used to compress the trace file. If the name\n  is \"none\", the data in the file is not compressed.\n\n The next 8 bytes are 64-bit integer, the offset within the file where\n the first OPTIONS section is located.\n\n The rest of the file consists of different sections. The only mandatory\n is the first OPTIONS section, all others are optional. The location and\n the order of the sections is not strict. Each section starts with a header:\n\nFORMAT OF THE SECTION HEADER\n----------------------------\n  <2 bytes> unsigned short integer, ID of the section.\n  <2 bytes> unsigned short integer, section flags:\n    1 = the section is compressed.\n  <4 bytes> ID of a string, description of the section.\n  <8 bytes> long long unsigned integer, size of the section in the file.\n\n  If the section is compressed, the above is the compressed size.\n  The section must be uncompressed on reading. The described format of\n  the sections refers to the uncompressed data.\n\nCOMPRESSION FORMAT OF THE FILE SECTIONS\n---------------------------------------\n\n  Some of the sections in the file may be compressed with the compression algorithm,\n  specified in the compression algorithm header. Compressed sections have a compression\n  header, written after the section header and right before the compressed data:\n    <4 bytes> unsigned int, size of compressed data in this section.\n    <4 bytes> unsigned int, size of uncompressed data.\n    <data> binary compressed data, with the specified size.\n\nCOMPRESSION FORMAT OF THE TRACE DATA\n------------------------------------\n\n  There are two special sections, BUFFER FLYRECORD and BUFFER LATENCY, containing\n  trace data. These sections may be compressed with the compression algorithm, specified\n  in the compression header. Usually the size of these sections is huge, that's why its\n  compression format is different from the other sections. The trace data is compressed\n  in chunks The size of one chunk is specified in the file creation time. The format\n  of compressed trace data is:\n     <4 bytes> unsigned int, count of chunks.\n     Follows the compressed chunks of given count. For each chunk:\n        <4 bytes> unsigned int, size of compressed data in this chunk.\n        <4 bytes> unsigned int, size of uncompressed data, aligned with the trace page size.\n        <data> binary compressed data, with the specified size.\n  These chunks must be uncompressed on reading. The described format of\n  trace data refers to the uncompressed data.\n\nOPTIONS SECTION\n---------------\n\n  Section ID: 0\n\n  This is the the only mandatory section in the file. There can be multiple\n  options sections, the first one is located at the offset specified right\n  after the compression algorithm header. The section consists of multiple\n  trace options, each option has the following format:\n    <2 bytes> unsigned short integer, ID of the option.\n    <4 bytes> unsigned integer, size of the option's data.\n    <binary data> bytes of the size specified above, data of the option.\n\n\n  Options, supported by the trace file version 7:\n\n  DONE: id 0, size 8\n    This option indicates the end of the options section, it is written\n    always as last option. The DONE option data is:\n       <8 bytes> long long unsigned integer, offset in the trace file where\n       the next options section is located. If this offset is 0, then there\n       are no more options sections.\n\n  DATE: id 1, size vary\n    The DATE option data is a null terminated ASCII string, which represents\n    the time difference between trace events timestamps and the Generic Time\n    of Day of the system.\n\n  CPUSTAT: id 2, size vary\n    The CPUSTAT option data is a null terminated ASCII string, the content of the\n    \"per_cpu/cpu<id>/stats\" file from the trace directory. There is a CPUSTAT option\n    for each CPU.\n\n  BUFFER: id 3, size vary\n    The BUFFER option describes the flyrecord trace data saved in the file, collected\n    from one trace instance. There is BUFFER option for each trace instance. The format\n    of the BUFFER data is:\n      <8 bytes> long long unsigned integer, offset in the trace file where the\n      BUFFER FLYRECORD section is located, containing flyrecord trace data.\n      <string> a null terminated ASCII string, name of the trace instance. Empty string \"\"\n      is saved as name of the top instance.\n      <string> a null terminated ASCII string, trace clock used for events timestamps in\n      this trace instance.\n      <4 bytes> unsigned integer, size of the trace buffer page.\n      <4 bytes> unsigned integer, count of the CPUs with trace data.\n      For each CPU of the above count:\n         <4 bytes> unsigned integer, ID of the CPU.\n         <8 bytes> long long unsigned integer, offset in the trace file where the trace data\n         for this CPU is located.\n         <8 bytes> long long unsigned integer, size of the trace data for this CPU.\n\n  TRACECLOCK: id 4, size vary\n    The TRACECLOCK option data is a null terminated ASCII string, the content of the\n    \"trace_clock\" file from the trace directory.\n\n  UNAME: id 5, size vary\n    The UNAME option data is a null terminated ASCII string, identifying the system where\n    the trace data is collected. The string is retrieved by the uname() system call.\n\n  HOOK: id 6, size vary\n    The HOOK option data is a null terminated ASCII string, describing event hooks: custom\n    event matching to connect any two events together.\n\n  OFFSET: id 7, size vary\n    The OFFSET option data is a null terminated ASCII string, representing a fixed time that\n    is added to each event timestamp on reading.\n\n  CPUCOUNT: id 8, size 4\n    The CPUCOUNT option data is:\n      <4 bytes> unsigned integer, number of CPUs in the system.\n\n  VERSION: id 9, size vary\n    The VERSION option data is a null terminated ASCII string, representing the version of\n    the trace-cmd application, used to collect these trace logs.\n\n  PROCMAPS: id 10, size vary\n    The PROCMAPS option data is a null terminated ASCII string, representing the memory map\n    of each traced filtered process. The format of the string is, for each filtered process:\n      <procss ID> <libraries count> <process command> \\n\n        <memory start address> <memory end address> <full path of the mapped library file> \\n\n        ...\n         separate line for each library, used by this process\n        ...\n      ...\n\n  TRACEID: id 11, size 8\n    The TRACEID option data is a unique identifier of this tracing session:\n      <8 bytes> long long unsigned integer, trace session identifier.\n\n  TIME_SHIFT: id 12, size vary\n    The TIME_SHIFT option stores time synchronization information, collected during host and guest\n    tracing session. Usually it is saved in the guest trace file. This information is used to\n    synchronize guest with host events timestamps, when displaying all files from this tracing\n    session. The format of the TIME_SHIFT option data is:\n      <8 bytes> long long unsigned integer, trace identifier of the peer (usually the host).\n      <4 bytes> unsigned integer, flags specific to the time synchronization protocol, used in this\n      trace session.\n      <4 bytes> unsigned integer, number of traced CPUs. For each CPU, timestamps corrections\n      are recorded:\n         <4 bytes> unsigned integer, count of the recorded timestamps corrections for this CPU.\n         <array of unsigned long long integers of the above count>, times when the corrections are calculated\n         <array of unsigned long long integers of the above count>, corrections offsets\n         <array of unsigned long long integers of the above count>, corrections scaling ratio\n\n  GUEST: id 13, size vary\n    The GUEST option stores information about traced guests in this tracing session. Usually it is\n    saved in the host trace file. There is a separate GUEST option for each traced guest.\n    The information is used when displaying all files from this tracing session. The format of\n    the GUEST option data is:\n       <string> a null terminated ASCII string, name of the guest.\n       <8 bytes> long long unsigned integer, trace identifier of the guest for this session.\n       <4 bytes> unsigned integer, number of guest's CPUs. For each CPU:\n          <4 bytes> unsigned integer, ID of the CPU.\n          <4 bytes> unsigned integer, PID of the host task, emulating this guest CPU.\n\n  TSC2NSEC: id 14, size 16\n    The TSC2NSEC option stores information, used to convert TSC events timestamps to nanoseconds.\n    The format of the TSC2NSEC option data is:\n       <4 bytes> unsigned integer, time multiplier.\n       <4 bytes> unsigned integer, time shift.\n       <8 bytes> unsigned long long integer, time offset.\n\n  STRINGS: id 15, size vary\n    The STRINGS option holds a list of nul terminated strings that holds the names of the\n    other sections.\n\n  HEADER_INFO: id 16, size 8\n    The HEADER_INFO option data is:\n      <8 bytes> long long unsigned integer, offset into the trace file where the HEADER INFO\n      section is located\n\n  FTRACE_EVENTS: id 17, size 8\n    The FTRACE_EVENTS option data is:\n      <8 bytes> long long unsigned integer, offset into the trace file where the\n      FTRACE EVENT FORMATS section is located.\n\n  EVENT_FORMATS: id 18, size 8\n    The EVENT_FORMATS option data is:\n      <8 bytes> long long unsigned integer, offset into the trace file where the EVENT FORMATS\n      section is located.\n\n  KALLSYMS: id 19, size 8\n    The KALLSYMS option data is:\n      <8 bytes> long long unsigned integer, offset into the trace file where the KALLSYMS\n      section is located.\n\n  PRINTK: id 20, size 8\n    The PRINTK option data is:\n      <8 bytes> long long unsigned integer, offset into the trace file where the TRACE_PRINTK\n      section is located.\n\n  CMDLINES: id 21, size 8\n    The CMDLINES option data is:\n      <8 bytes> long long unsigned integer, offset into the trace file where the\n      SAVED COMMAND LINES section is located.\n\n  BUFFER_TEXT: id 22, size\n    The BUFFER_LAT option describes the latency trace data saved in the file. The format\n    of the BUFFER_LAT data is:\n      <8 bytes> long long unsigned integer, offset in the trace file where the\n      BUFFER LATENCY section is located, containing latency trace data.\n      <string> a null terminated ASCII string, name of the trace instance. Empty string \"\"\n      is saved as name of the top instance.\n      <string> a null terminated ASCII string, trace clock used for events timestamps in\n      this trace instance.\n\n  BTF_FILE: id 23, size 8\n    The BTF_FILE option data is:\n     A compressed data from the host file /sys/kernel/btf/vmlinux, using the\n     compression algorthim defined by the trace.dat header. The compression data\n     includes the size of the uncompressed output.\n\n  LAST_BOOT_INFO: id 24, size vary\n    The LAST_BOOT_INFO option data contains the content of the last_boot_info if\n     the file existed for an instance. It starts with the instance name, followed\n     by a colon \":\" and then the content of the last_boot_info file. Currently\n     the instance name is not used, but exists in case in the future there are\n     more than one instance with this file.\n\n  MODULES_FILE: id 25, size 8\n    The MODULES_FILE option data is:\n     A compressed data from the host file /proc/modules, using the compression\n     algorthim defined by the trace.dat header. The compression data includes\n     the size of the uncompressed output.\n\nHEADER INFO SECTION\n-------------------\n\n  Section ID: 16\n\n  The first 12 bytes of the section, after the section header, contain the string:\n\n    \"header_page\\0\"\n\n  The next 8 bytes are a 64-bit word containing the size of the\n  page header information stored next.\n\n  The next set of data is of the size read from the previous 8 bytes,\n  and contains the data retrieved from debugfs/tracing/events/header_page.\n\n  Note: The size of the second field \\fBcommit\\fR contains the target\n  kernel long size. For example:\n\n  field: local_t commit;\toffset:8;\t\\fBsize:8;\\fR\tsigned:1;\n\n  shows the kernel has a 64-bit long.\n\n  The next 13 bytes contain the string:\n\n  \"header_event\\0\"\n\n  The next 8 bytes are a 64-bit word containing the size of the\n  event header information stored next.\n\n  The next set of data is of the size read from the previous 8 bytes\n  and contains the data retrieved from debugfs/tracing/events/header_event.\n\n  This data allows the trace-cmd tool to know if the ring buffer format\n  of the kernel made any changes.\n\nFTRACE EVENT FORMATS SECTION\n----------------------------\n\n  Section ID: 17\n\n  Directly after the section header comes the information about\n  the Ftrace specific events. These are the events used by the Ftrace plugins\n  and are not enabled by the event tracing.\n\n  The next 4 bytes contain a 32-bit word of the number of Ftrace event\n  format files that are stored in the file.\n\n  For the number of times defined by the previous 4 bytes is the\n  following:\n\n  8 bytes for the size of the Ftrace event format file.\n\n  The Ftrace event format file copied from the target machine:\n  debugfs/tracing/events/ftrace/<event>/format\n\nEVENT FORMATS SECTION\n---------------------\n\n  Section ID: 18\n\n  Directly after the section header comes the information about\n  the event layout.\n\n  The next 4 bytes are a 32-bit word containing the number of\n  event systems that are stored in the file. These are the\n  directories in debugfs/tracing/events excluding the \\fBftrace\\fR\n  directory.\n\n  For the number of times defined by the previous 4 bytes is the\n  following:\n\n  A null-terminated string containing the system name.\n\n  4 bytes containing a 32-bit word containing the number\n  of events within the system.\n\n  For the number of times defined in the previous 4 bytes is the\n  following:\n\n  8 bytes for the size of the event format file.\n\n  The event format file copied from the target machine:\n  debugfs/tracing/events/<system>/<event>/format\n\nKALLSYMS SECTION\n----------------\n\n  Section ID: 19\n\n  Directly after the section header comes the information of the mapping\n  of function addresses to the function names.\n\n  The next 4 bytes are a 32-bit word containing the size of the\n  data holding the function mappings.\n\n  The next set of data is of the size defined by the previous 4 bytes\n  and contains the information from the target machine's file:\n  /proc/kallsyms\n\n\nTRACE_PRINTK SECTION\n--------------------\n\n  Section ID: 20\n\n  If a developer used trace_printk() within the kernel, it may\n  store the format string outside the ring buffer.\n  This information can be found in:\n  debugfs/tracing/printk_formats\n\n  The next 4 bytes are a 32-bit word containing the size of the\n  data holding the printk formats.\n\n  The next set of data is of the size defined by the previous 4 bytes\n  and contains the information from debugfs/tracing/printk_formats.\n\n\nSAVED COMMAND LINES SECTION\n---------------------------\n\n  Section ID: 21\n\n  Directly after the section header comes the information mapping\n  a PID to a process name.\n\n  The next 8 bytes contain a 64-bit word that holds the size of the\n  data mapping the PID to a process name.\n\n  The next set of data is of the size defined by the previous 8 bytes\n  and contains the information from debugfs/tracing/saved_cmdlines.\n\n\nBUFFER FLYRECORD SECTION\n------------------------\n\n  This section contains flyrecord tracing data, collected in one trace instance.\n  The data is saved per CPU. Each BUFFER FLYRECORD section has a corresponding BUFFER\n  option, containing information about saved CPU's trace data. Padding is placed between\n  the section header and the CPU data, placing the CPU data at a page aligned (target page)\n  position in the file.\n\n  This data is copied directly from the Ftrace ring buffer and is of the\n  same format as the ring buffer specified by the event header files\n  loaded in the header format file.\n\n  The trace-cmd tool will try to \\fBmmap(2)\\fR the data page by page with the\n  target's page size if possible. If it fails to mmap, it will just read the\n  data instead.\n\nBUFFER TEXT SECTION\n------------------------\n\n  Section ID: 22\n\n  This section contains latency tracing data, ASCII text taken from the\n  target's debugfs/tracing/trace file.\n\nSTRINGS SECTION\n------------------------\n\n  Section ID: 15\n\n  All strings of the trace file metadata are stored in a string section within the file. The section\n  contains a list of nul terminated ASCII strings. An ID of the string is used in the file\n  meta data, which is the offset of the actual string into the string section. Strings can be stored\n  into multiple string sections in the file.\n\nBTF FILE SECTION\n----------------\n\n  Section ID: 23\n\n  Directly after the section header comes the a 8 byte value that contains\n  a 64-bit word that holds he size of the BTF file (may be compressed)\n\n  The next set of data is of the size defined by the previous 8 bytes\n  and contains the information from /sys/kernel/btf/vmlinux.\n\nMODULES FILE SECTION\n--------------------\n\n  Section ID: 25\n\n  Directly after the section header comes the a 8 byte value that contains\n  a 64-bit word that holds he size of the modules file (may be compressed)\n\n  The next set of data is of the size defined by the previous 8 bytes\n  and contains the information from /proc/modules.\n\nSEE ALSO\n--------\ntrace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),\ntrace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),\ntrace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1),\ntrace-cmd.dat(5)\n\nAUTHOR\n------\nWritten by Steven Rostedt, <rostedt@goodmis.org>\n\nRESOURCES\n---------\nhttps://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\n\nCOPYING\n-------\nCopyright \\(C) 2010 Red Hat, Inc. Free use of this software is granted under\nthe terms of the GNU Public License (GPL).\n"
  },
  {
    "path": "LICENSES/GPL-2.0",
    "content": "Valid-License-Identifier: GPL-2.0\nValid-License-Identifier: GPL-2.0-only\nValid-License-Identifier: GPL-2.0+\nValid-License-Identifier: GPL-2.0-or-later\nSPDX-URL: https://spdx.org/licenses/GPL-2.0.html\nUsage-Guide:\n  To use this license in source code, put one of the following SPDX\n  tag/value pairs into a comment according to the placement\n  guidelines in the licensing rules documentation.\n  For 'GNU General Public License (GPL) version 2 only' use:\n    SPDX-License-Identifier: GPL-2.0\n  or\n    SPDX-License-Identifier: GPL-2.0-only\n  For 'GNU General Public License (GPL) version 2 or any later version' use:\n    SPDX-License-Identifier: GPL-2.0+\n  or\n    SPDX-License-Identifier: GPL-2.0-or-later\nLicense-Text:\n\n\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\f\n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\f\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\f\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\f\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\f\n\t    How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program; if not, write to the Free Software\n    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Library General\nPublic License instead of this License.\n"
  },
  {
    "path": "LICENSES/LGPL-2.1",
    "content": "Valid-License-Identifier: LGPL-2.1\nValid-License-Identifier: LGPL-2.1+\nSPDX-URL: https://spdx.org/licenses/LGPL-2.1.html\nUsage-Guide:\n  To use this license in source code, put one of the following SPDX\n  tag/value pairs into a comment according to the placement\n  guidelines in the licensing rules documentation.\n  For 'GNU Lesser General Public License (LGPL) version 2.1 only' use:\n    SPDX-License-Identifier: LGPL-2.1\n  For 'GNU Lesser General Public License (LGPL) version 2.1 or any later\n  version' use:\n    SPDX-License-Identifier: LGPL-2.1+\nLicense-Text:\n\nGNU LESSER GENERAL PUBLIC LICENSE\nVersion 2.1, February 1999\n\nCopyright (C) 1991, 1999 Free Software Foundation, Inc.\n51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n[This is the first released version of the Lesser GPL. It also counts as\nthe successor of the GNU Library Public License, version 2, hence the\nversion number 2.1.]\n\nPreamble\n\nThe licenses for most software are designed to take away your freedom to\nshare and change it. By contrast, the GNU General Public Licenses are\nintended to guarantee your freedom to share and change free software--to\nmake sure the software is free for all its users.\n\nThis license, the Lesser General Public License, applies to some specially\ndesignated software packages--typically libraries--of the Free Software\nFoundation and other authors who decide to use it. You can use it too, but\nwe suggest you first think carefully about whether this license or the\nordinary General Public License is the better strategy to use in any\nparticular case, based on the explanations below.\n\nWhen we speak of free software, we are referring to freedom of use, not\nprice. Our General Public Licenses are designed to make sure that you have\nthe freedom to distribute copies of free software (and charge for this\nservice if you wish); that you receive source code or can get it if you\nwant it; that you can change the software and use pieces of it in new free\nprograms; and that you are informed that you can do these things.\n\nTo protect your rights, we need to make restrictions that forbid\ndistributors to deny you these rights or to ask you to surrender these\nrights. These restrictions translate to certain responsibilities for you if\nyou distribute copies of the library or if you modify it.\n\nFor example, if you distribute copies of the library, whether gratis or for\na fee, you must give the recipients all the rights that we gave you. You\nmust make sure that they, too, receive or can get the source code. If you\nlink other code with the library, you must provide complete object files to\nthe recipients, so that they can relink them with the library after making\nchanges to the library and recompiling it. And you must show them these\nterms so they know their rights.\n\nWe protect your rights with a two-step method: (1) we copyright the\nlibrary, and (2) we offer you this license, which gives you legal\npermission to copy, distribute and/or modify the library.\n\nTo protect each distributor, we want to make it very clear that there is no\nwarranty for the free library. Also, if the library is modified by someone\nelse and passed on, the recipients should know that what they have is not\nthe original version, so that the original author's reputation will not be\naffected by problems that might be introduced by others.\n\nFinally, software patents pose a constant threat to the existence of any\nfree program. We wish to make sure that a company cannot effectively\nrestrict the users of a free program by obtaining a restrictive license\nfrom a patent holder. Therefore, we insist that any patent license obtained\nfor a version of the library must be consistent with the full freedom of\nuse specified in this license.\n\nMost GNU software, including some libraries, is covered by the ordinary GNU\nGeneral Public License. This license, the GNU Lesser General Public\nLicense, applies to certain designated libraries, and is quite different\nfrom the ordinary General Public License. We use this license for certain\nlibraries in order to permit linking those libraries into non-free\nprograms.\n\nWhen a program is linked with a library, whether statically or using a\nshared library, the combination of the two is legally speaking a combined\nwork, a derivative of the original library. The ordinary General Public\nLicense therefore permits such linking only if the entire combination fits\nits criteria of freedom. The Lesser General Public License permits more lax\ncriteria for linking other code with the library.\n\nWe call this license the \"Lesser\" General Public License because it does\nLess to protect the user's freedom than the ordinary General Public\nLicense. It also provides other free software developers Less of an\nadvantage over competing non-free programs. These disadvantages are the\nreason we use the ordinary General Public License for many\nlibraries. However, the Lesser license provides advantages in certain\nspecial circumstances.\n\nFor example, on rare occasions, there may be a special need to encourage\nthe widest possible use of a certain library, so that it becomes a de-facto\nstandard. To achieve this, non-free programs must be allowed to use the\nlibrary. A more frequent case is that a free library does the same job as\nwidely used non-free libraries. In this case, there is little to gain by\nlimiting the free library to free software only, so we use the Lesser\nGeneral Public License.\n\nIn other cases, permission to use a particular library in non-free programs\nenables a greater number of people to use a large body of free\nsoftware. For example, permission to use the GNU C Library in non-free\nprograms enables many more people to use the whole GNU operating system, as\nwell as its variant, the GNU/Linux operating system.\n\nAlthough the Lesser General Public License is Less protective of the users'\nfreedom, it does ensure that the user of a program that is linked with the\nLibrary has the freedom and the wherewithal to run that program using a\nmodified version of the Library.\n\nThe precise terms and conditions for copying, distribution and modification\nfollow. Pay close attention to the difference between a \"work based on the\nlibrary\" and a \"work that uses the library\". The former contains code\nderived from the library, whereas the latter must be combined with the\nlibrary in order to run.\n\nTERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n0. This License Agreement applies to any software library or other program\n   which contains a notice placed by the copyright holder or other\n   authorized party saying it may be distributed under the terms of this\n   Lesser General Public License (also called \"this License\"). Each\n   licensee is addressed as \"you\".\n\n   A \"library\" means a collection of software functions and/or data\n   prepared so as to be conveniently linked with application programs\n   (which use some of those functions and data) to form executables.\n\n   The \"Library\", below, refers to any such software library or work which\n   has been distributed under these terms. A \"work based on the Library\"\n   means either the Library or any derivative work under copyright law:\n   that is to say, a work containing the Library or a portion of it, either\n   verbatim or with modifications and/or translated straightforwardly into\n   another language. (Hereinafter, translation is included without\n   limitation in the term \"modification\".)\n\n   \"Source code\" for a work means the preferred form of the work for making\n   modifications to it. For a library, complete source code means all the\n   source code for all modules it contains, plus any associated interface\n   definition files, plus the scripts used to control compilation and\n   installation of the library.\n\n    Activities other than copying, distribution and modification are not\n    covered by this License; they are outside its scope. The act of running\n    a program using the Library is not restricted, and output from such a\n    program is covered only if its contents constitute a work based on the\n    Library (independent of the use of the Library in a tool for writing\n    it). Whether that is true depends on what the Library does and what the\n    program that uses the Library does.\n\n1. You may copy and distribute verbatim copies of the Library's complete\n   source code as you receive it, in any medium, provided that you\n   conspicuously and appropriately publish on each copy an appropriate\n   copyright notice and disclaimer of warranty; keep intact all the notices\n   that refer to this License and to the absence of any warranty; and\n   distribute a copy of this License along with the Library.\n\n   You may charge a fee for the physical act of transferring a copy, and\n   you may at your option offer warranty protection in exchange for a fee.\n\n2. You may modify your copy or copies of the Library or any portion of it,\n   thus forming a work based on the Library, and copy and distribute such\n   modifications or work under the terms of Section 1 above, provided that\n   you also meet all of these conditions:\n\n   a) The modified work must itself be a software library.\n\n   b) You must cause the files modified to carry prominent notices stating\n      that you changed the files and the date of any change.\n\n   c) You must cause the whole of the work to be licensed at no charge to\n      all third parties under the terms of this License.\n\n   d) If a facility in the modified Library refers to a function or a table\n      of data to be supplied by an application program that uses the\n      facility, other than as an argument passed when the facility is\n      invoked, then you must make a good faith effort to ensure that, in\n      the event an application does not supply such function or table, the\n      facility still operates, and performs whatever part of its purpose\n      remains meaningful.\n\n   (For example, a function in a library to compute square roots has a\n    purpose that is entirely well-defined independent of the\n    application. Therefore, Subsection 2d requires that any\n    application-supplied function or table used by this function must be\n    optional: if the application does not supply it, the square root\n    function must still compute square roots.)\n\n   These requirements apply to the modified work as a whole. If\n   identifiable sections of that work are not derived from the Library, and\n   can be reasonably considered independent and separate works in\n   themselves, then this License, and its terms, do not apply to those\n   sections when you distribute them as separate works. But when you\n   distribute the same sections as part of a whole which is a work based on\n   the Library, the distribution of the whole must be on the terms of this\n   License, whose permissions for other licensees extend to the entire\n   whole, and thus to each and every part regardless of who wrote it.\n\n   Thus, it is not the intent of this section to claim rights or contest\n   your rights to work written entirely by you; rather, the intent is to\n   exercise the right to control the distribution of derivative or\n   collective works based on the Library.\n\n   In addition, mere aggregation of another work not based on the Library\n   with the Library (or with a work based on the Library) on a volume of a\n   storage or distribution medium does not bring the other work under the\n   scope of this License.\n\n3. You may opt to apply the terms of the ordinary GNU General Public\n   License instead of this License to a given copy of the Library. To do\n   this, you must alter all the notices that refer to this License, so that\n   they refer to the ordinary GNU General Public License, version 2,\n   instead of to this License. (If a newer version than version 2 of the\n   ordinary GNU General Public License has appeared, then you can specify\n   that version instead if you wish.) Do not make any other change in these\n   notices.\n\n   Once this change is made in a given copy, it is irreversible for that\n   copy, so the ordinary GNU General Public License applies to all\n   subsequent copies and derivative works made from that copy.\n\n   This option is useful when you wish to copy part of the code of the\n   Library into a program that is not a library.\n\n4. You may copy and distribute the Library (or a portion or derivative of\n   it, under Section 2) in object code or executable form under the terms\n   of Sections 1 and 2 above provided that you accompany it with the\n   complete corresponding machine-readable source code, which must be\n   distributed under the terms of Sections 1 and 2 above on a medium\n   customarily used for software interchange.\n\n   If distribution of object code is made by offering access to copy from a\n   designated place, then offering equivalent access to copy the source\n   code from the same place satisfies the requirement to distribute the\n   source code, even though third parties are not compelled to copy the\n   source along with the object code.\n\n5. A program that contains no derivative of any portion of the Library, but\n   is designed to work with the Library by being compiled or linked with\n   it, is called a \"work that uses the Library\". Such a work, in isolation,\n   is not a derivative work of the Library, and therefore falls outside the\n   scope of this License.\n\n   However, linking a \"work that uses the Library\" with the Library creates\n   an executable that is a derivative of the Library (because it contains\n   portions of the Library), rather than a \"work that uses the\n   library\". The executable is therefore covered by this License. Section 6\n   states terms for distribution of such executables.\n\n   When a \"work that uses the Library\" uses material from a header file\n   that is part of the Library, the object code for the work may be a\n   derivative work of the Library even though the source code is\n   not. Whether this is true is especially significant if the work can be\n   linked without the Library, or if the work is itself a library. The\n   threshold for this to be true is not precisely defined by law.\n\n   If such an object file uses only numerical parameters, data structure\n   layouts and accessors, and small macros and small inline functions (ten\n   lines or less in length), then the use of the object file is\n   unrestricted, regardless of whether it is legally a derivative\n   work. (Executables containing this object code plus portions of the\n   Library will still fall under Section 6.)\n\n   Otherwise, if the work is a derivative of the Library, you may\n   distribute the object code for the work under the terms of Section\n   6. Any executables containing that work also fall under Section 6,\n   whether or not they are linked directly with the Library itself.\n\n6. As an exception to the Sections above, you may also combine or link a\n   \"work that uses the Library\" with the Library to produce a work\n   containing portions of the Library, and distribute that work under terms\n   of your choice, provided that the terms permit modification of the work\n   for the customer's own use and reverse engineering for debugging such\n   modifications.\n\n   You must give prominent notice with each copy of the work that the\n   Library is used in it and that the Library and its use are covered by\n   this License. You must supply a copy of this License. If the work during\n   execution displays copyright notices, you must include the copyright\n   notice for the Library among them, as well as a reference directing the\n   user to the copy of this License. Also, you must do one of these things:\n\n   a) Accompany the work with the complete corresponding machine-readable\n      source code for the Library including whatever changes were used in\n      the work (which must be distributed under Sections 1 and 2 above);\n      and, if the work is an executable linked with the Library, with the\n      complete machine-readable \"work that uses the Library\", as object\n      code and/or source code, so that the user can modify the Library and\n      then relink to produce a modified executable containing the modified\n      Library. (It is understood that the user who changes the contents of\n      definitions files in the Library will not necessarily be able to\n      recompile the application to use the modified definitions.)\n\n   b) Use a suitable shared library mechanism for linking with the\n      Library. A suitable mechanism is one that (1) uses at run time a copy\n      of the library already present on the user's computer system, rather\n      than copying library functions into the executable, and (2) will\n      operate properly with a modified version of the library, if the user\n      installs one, as long as the modified version is interface-compatible\n      with the version that the work was made with.\n\n   c) Accompany the work with a written offer, valid for at least three\n      years, to give the same user the materials specified in Subsection\n      6a, above, for a charge no more than the cost of performing this\n      distribution.\n\n   d) If distribution of the work is made by offering access to copy from a\n      designated place, offer equivalent access to copy the above specified\n      materials from the same place.\n\n   e) Verify that the user has already received a copy of these materials\n      or that you have already sent this user a copy.\n\n   For an executable, the required form of the \"work that uses the Library\"\n   must include any data and utility programs needed for reproducing the\n   executable from it. However, as a special exception, the materials to be\n   distributed need not include anything that is normally distributed (in\n   either source or binary form) with the major components (compiler,\n   kernel, and so on) of the operating system on which the executable runs,\n   unless that component itself accompanies the executable.\n\n   It may happen that this requirement contradicts the license restrictions\n   of other proprietary libraries that do not normally accompany the\n   operating system. Such a contradiction means you cannot use both them\n   and the Library together in an executable that you distribute.\n\n7. You may place library facilities that are a work based on the Library\n   side-by-side in a single library together with other library facilities\n   not covered by this License, and distribute such a combined library,\n   provided that the separate distribution of the work based on the Library\n   and of the other library facilities is otherwise permitted, and provided\n   that you do these two things:\n\n   a) Accompany the combined library with a copy of the same work based on\n      the Library, uncombined with any other library facilities. This must\n      be distributed under the terms of the Sections above.\n\n   b) Give prominent notice with the combined library of the fact that part\n      of it is a work based on the Library, and explaining where to find\n      the accompanying uncombined form of the same work.\n\n8. You may not copy, modify, sublicense, link with, or distribute the\n   Library except as expressly provided under this License. Any attempt\n   otherwise to copy, modify, sublicense, link with, or distribute the\n   Library is void, and will automatically terminate your rights under this\n   License. However, parties who have received copies, or rights, from you\n   under this License will not have their licenses terminated so long as\n   such parties remain in full compliance.\n\n9. You are not required to accept this License, since you have not signed\n   it. However, nothing else grants you permission to modify or distribute\n   the Library or its derivative works. These actions are prohibited by law\n   if you do not accept this License. Therefore, by modifying or\n   distributing the Library (or any work based on the Library), you\n   indicate your acceptance of this License to do so, and all its terms and\n   conditions for copying, distributing or modifying the Library or works\n   based on it.\n\n10. Each time you redistribute the Library (or any work based on the\n    Library), the recipient automatically receives a license from the\n    original licensor to copy, distribute, link with or modify the Library\n    subject to these terms and conditions. You may not impose any further\n    restrictions on the recipients' exercise of the rights granted\n    herein. You are not responsible for enforcing compliance by third\n    parties with this License.\n\n11. If, as a consequence of a court judgment or allegation of patent\n    infringement or for any other reason (not limited to patent issues),\n    conditions are imposed on you (whether by court order, agreement or\n    otherwise) that contradict the conditions of this License, they do not\n    excuse you from the conditions of this License. If you cannot\n    distribute so as to satisfy simultaneously your obligations under this\n    License and any other pertinent obligations, then as a consequence you\n    may not distribute the Library at all. For example, if a patent license\n    would not permit royalty-free redistribution of the Library by all\n    those who receive copies directly or indirectly through you, then the\n    only way you could satisfy both it and this License would be to refrain\n    entirely from distribution of the Library.\n\n    If any portion of this section is held invalid or unenforceable under\n    any particular circumstance, the balance of the section is intended to\n    apply, and the section as a whole is intended to apply in other\n    circumstances.\n\n    It is not the purpose of this section to induce you to infringe any\n    patents or other property right claims or to contest validity of any\n    such claims; this section has the sole purpose of protecting the\n    integrity of the free software distribution system which is implemented\n    by public license practices. Many people have made generous\n    contributions to the wide range of software distributed through that\n    system in reliance on consistent application of that system; it is up\n    to the author/donor to decide if he or she is willing to distribute\n    software through any other system and a licensee cannot impose that\n    choice.\n\n    This section is intended to make thoroughly clear what is believed to\n    be a consequence of the rest of this License.\n\n12. If the distribution and/or use of the Library is restricted in certain\n    countries either by patents or by copyrighted interfaces, the original\n    copyright holder who places the Library under this License may add an\n    explicit geographical distribution limitation excluding those\n    countries, so that distribution is permitted only in or among countries\n    not thus excluded. In such case, this License incorporates the\n    limitation as if written in the body of this License.\n\n13. The Free Software Foundation may publish revised and/or new versions of\n    the Lesser General Public License from time to time. Such new versions\n    will be similar in spirit to the present version, but may differ in\n    detail to address new problems or concerns.\n\n    Each version is given a distinguishing version number. If the Library\n    specifies a version number of this License which applies to it and \"any\n    later version\", you have the option of following the terms and\n    conditions either of that version or of any later version published by\n    the Free Software Foundation. If the Library does not specify a license\n    version number, you may choose any version ever published by the Free\n    Software Foundation.\n\n14. If you wish to incorporate parts of the Library into other free\n    programs whose distribution conditions are incompatible with these,\n    write to the author to ask for permission. For software which is\n    copyrighted by the Free Software Foundation, write to the Free Software\n    Foundation; we sometimes make exceptions for this. Our decision will be\n    guided by the two goals of preserving the free status of all\n    derivatives of our free software and of promoting the sharing and reuse\n    of software generally.\n\nNO WARRANTY\n\n15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n    FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n    OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n    PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER\n    EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE\n    ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH\n    YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL\n    NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n    REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR\n    DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL\n    DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY\n    (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED\n    INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF\n    THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR\n    OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\nEND OF TERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Libraries\n\nIf you develop a new library, and you want it to be of the greatest\npossible use to the public, we recommend making it free software that\neveryone can redistribute and change. You can do so by permitting\nredistribution under these terms (or, alternatively, under the terms of the\nordinary General Public License).\n\nTo apply these terms, attach the following notices to the library. It is\nsafest to attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least the\n\"copyright\" line and a pointer to where the full notice is found.\n\none line to give the library's name and an idea of what it does.\nCopyright (C) year name of author\n\nThis library is free software; you can redistribute it and/or modify it\nunder the terms of the GNU Lesser General Public License as published by\nthe Free Software Foundation; either version 2.1 of the License, or (at\nyour option) any later version.\n\nThis library is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\nFITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License\nfor more details.\n\nYou should have received a copy of the GNU Lesser General Public License\nalong with this library; if not, write to the Free Software Foundation,\nInc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add\ninformation on how to contact you by electronic and paper mail.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the library, if\nnecessary. Here is a sample; alter the names:\n\nYoyodyne, Inc., hereby disclaims all copyright interest in\nthe library `Frob' (a library for tweaking knobs) written\nby James Random Hacker.\n\nsignature of Ty Coon, 1 April 1990\nTy Coon, President of Vice\nThat's all there is to it!\n"
  },
  {
    "path": "Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n# trace-cmd version\nTC_VERSION = 3\nTC_PATCHLEVEL = 4\nTC_EXTRAVERSION = 0\nTRACECMD_VERSION = $(TC_VERSION).$(TC_PATCHLEVEL).$(TC_EXTRAVERSION)\n\nexport TC_VERSION\nexport TC_PATCHLEVEL\nexport TC_EXTRAVERSION\nexport TRACECMD_VERSION\n\nLIBTC_VERSION = 1\nLIBTC_PATCHLEVEL = 5\nLIBTC_EXTRAVERSION = 5\nLIBTRACECMD_VERSION = $(LIBTC_VERSION).$(LIBTC_PATCHLEVEL).$(LIBTC_EXTRAVERSION)\n\nexport LIBTC_VERSION\nexport LIBTC_PATCHLEVEL\nexport LIBTC_EXTRAVERSION\nexport LIBTRACECMD_VERSION\n\nVERSION_FILE = ltc_version.h\n\nLIBTRACEEVENT_MIN_VERSION = 1.9\nLIBTRACEFS_MIN_VERSION = 1.8\n\nMAKEFLAGS += --no-print-directory\n\n# Makefiles suck: This macro sets a default value of $(2) for the\n# variable named by $(1), unless the variable has been set by\n# environment or command line. This is necessary for CC and AR\n# because make sets default values, so the simpler ?= approach\n# won't work as expected.\ndefine allow-override\n  $(if $(or $(findstring environment,$(origin $(1))),\\\n            $(findstring command line,$(origin $(1)))),,\\\n    $(eval $(1) = $(2)))\nendef\n\n# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.\n$(call allow-override,CC,$(CROSS_COMPILE)gcc)\n$(call allow-override,AR,$(CROSS_COMPILE)ar)\n$(call allow-override,PKG_CONFIG,pkg-config)\n$(call allow-override,LD_SO_CONF_PATH,/etc/ld.so.conf.d/)\n$(call allow-override,LDCONFIG,ldconfig)\n\nexport LD_SO_CONF_PATH LDCONFIG\n\nEXT = -std=gnu99\nINSTALL = install\n\n# Use DESTDIR for installing into a different root directory.\n# This is useful for building a package. The program will be\n# installed in this directory as if it was the root directory.\n# Then the build tool can move it later.\nDESTDIR ?=\nDESTDIR_SQ = '$(subst ','\\'',$(DESTDIR))'\n\nLP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)\nifeq ($(LP64), 1)\n  libdir_relative_temp = lib64\nelse\n  libdir_relative_temp = lib\nendif\n\nlibdir_relative ?= $(libdir_relative_temp)\nprefix ?= /usr/local\nbindir_relative = bin\nbindir = $(prefix)/$(bindir_relative)\nman_dir = $(prefix)/share/man\nman_dir_SQ = '$(subst ','\\'',$(man_dir))'\ncompletion_dir = $(prefix)/share/bash-completion/completions\ncompletion_dir_SQ = '$(subst ','\\'',$(completion_dir))'\nhtml_install_SQ = '$(subst ','\\'',$(html_install))'\nimg_install_SQ = '$(subst ','\\'',$(img_install))'\nlibdir = $(prefix)/$(libdir_relative)\nlibdir_SQ = '$(subst ','\\'',$(libdir))'\nincludedir = $(prefix)/include\nincludedir_SQ = '$(subst ','\\'',$(includedir))'\npkgconfig_dir ?= $(word 1,$(shell $(PKG_CONFIG) \t\t\\\n\t\t\t--variable pc_path pkg-config | tr \":\" \" \"))\n\nexport man_dir man_dir_SQ html_install html_install_SQ INSTALL\nexport img_install img_install_SQ libdir libdir_SQ includedir_SQ\nexport DESTDIR DESTDIR_SQ\n\nifeq ($(prefix),$(HOME))\nplugin_tracecmd_dir = $(libdir)/trace-cmd/plugins\npython_dir ?= $(libdir)/trace-cmd/python\nvar_dir = $(HOME)/.trace-cmd/\nelse\npython_dir ?= $(libdir)/trace-cmd/python\nPLUGIN_DIR_TRACECMD = -DPLUGIN_TRACECMD_DIR=\"$(plugin_tracecmd_dir)\"\nPYTHON_DIR = -DPYTHON_DIR=\"$(python_dir)\"\nPLUGIN_DIR_TRACECMD_SQ = '$(subst ','\\'',$(PLUGIN_DIR_TRACECMD))'\nPYTHON_DIR_SQ = '$(subst ','\\'',$(PYTHON_DIR))'\nvar_dir = /var\nendif\n\n# Shell quotes\nbindir_SQ = $(subst ','\\'',$(bindir))\nbindir_relative_SQ = $(subst ','\\'',$(bindir_relative))\nplugin_tracecmd_dir_SQ = $(subst ','\\'',$(plugin_tracecmd_dir))\npython_dir_SQ = $(subst ','\\'',$(python_dir))\n\npound := \\#\n\nVAR_DIR = -DVAR_DIR=\"$(var_dir)\"\nVAR_DIR_SQ = '$(subst ','\\'',$(VAR_DIR))'\nvar_dir_SQ = '$(subst ','\\'',$(var_dir))'\n\nHELP_DIR = -DHELP_DIR=$(html_install)\nHELP_DIR_SQ = '$(subst ','\\'',$(HELP_DIR))'\n#' emacs highlighting gets confused by the above escaped quote.\n\nexport PLUGIN_DIR_TRACECMD\nexport PYTHON_DIR\nexport PYTHON_DIR_SQ\nexport plugin_tracecmd_dir_SQ\nexport python_dir_SQ\nexport var_dir\n\n# copy a bit from Linux kbuild\n\nifeq (\"$(origin V)\", \"command line\")\n  VERBOSE = $(V)\nendif\nifndef VERBOSE\n  VERBOSE = 0\nendif\n\nSILENT := $(if $(findstring s,$(filter-out --%,$(MAKEFLAGS))),1)\n\nSWIG_DEFINED := $(shell if command -v swig; then echo 1; else echo 0; fi)\nifeq ($(SWIG_DEFINED), 0)\nBUILD_PYTHON := report_noswig\nNO_PYTHON = 1\nendif\n\nifndef NO_PYTHON\nPYTHON\t\t:= ctracecmd.so\n\nPYTHON_VERS ?= python3\nPYTHON_PKGCONFIG_VERS ?= $(PYTHON_VERS)\n\n# Can build python?\nifeq ($(shell sh -c \"$(PKG_CONFIG) --cflags $(PYTHON_PKGCONFIG_VERS) > /dev/null 2>&1 && echo y\"), y)\n\tBUILD_PYTHON := $(PYTHON)\n\tBUILD_PYTHON_WORKS := 1\nelse\n\tBUILD_PYTHON := report_nopythondev\n\tNO_PYTHON = 1\nendif\nendif # NO_PYTHON\n\nexport BUILD_PYTHON_WORKS\nexport NO_PYTHON\n\n# $(call test-build, snippet, ret) -> ret if snippet compiles\n#                                  -> empty otherwise\ntest-build = $(if $(shell sh -c 'echo \"$(1)\" | \\\n\t$(CC) -o /dev/null -x c - > /dev/null 2>&1 && echo y'), $2)\n\nUDIS86_AVAILABLE := $(call test-build,\\#include <udis86.h>, y)\nifneq ($(strip $(UDIS86_AVAILABLE)), y)\nNO_UDIS86 := 1\nendif\n\nifndef NO_UDIS86\n# have udis86 disassembler library?\nudis86-flags := -DHAVE_UDIS86 -ludis86\nudis86-ldflags := -ludis86\nendif # NO_UDIS86\n\ndefine BLK_TC_FLUSH_SOURCE\n#include <linux/blktrace_api.h>\nint main(void) { return BLK_TC_FLUSH; }\nendef\n\n# have flush/fua block layer instead of barriers?\nblk-flags := $(call test-build,$(BLK_TC_FLUSH_SOURCE),-DHAVE_BLK_TC_FLUSH)\n\ndefine MEMFD_CREATE_SOURCE\n#define _GNU_SOURCE\n#include <sys/mman.h>\nint main(void) { return memfd_create(\\\"test\\\", 0); }\nendef\n\n# have memfd_create available\nmemfd-flags := $(call test-build,$(MEMFD_CREATE_SOURCE),-DHAVE_MEMFD_CREATE)\n\nifeq (\"$(origin O)\", \"command line\")\n\n  saved-output := $(O)\n  BUILD_OUTPUT := $(shell cd $(O) && /bin/pwd)\n  $(if $(BUILD_OUTPUT),, \\\n    $(error output directory \"$(saved-output)\" does not exist))\n\nelse\n  BUILD_OUTPUT = $(CURDIR)\nendif\n\nsrctree\t\t:= $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR))\nobjtree\t\t:= $(BUILD_OUTPUT)\nsrc\t\t:= $(srctree)\nobj\t\t:= $(objtree)\n\nPKG_CONFIG_SOURCE_FILE = libtracecmd.pc\nPKG_CONFIG_FILE := $(addprefix $(BUILD_OUTPUT)/,$(PKG_CONFIG_SOURCE_FILE))\n\nexport pkgconfig_dir PKG_CONFIG_FILE\n\nexport prefix bindir src obj\n\nLIBS ?= -ldl\n\nLIBTRACECMD_DIR = $(obj)/lib/trace-cmd\nLIBTRACECMD_STATIC = $(LIBTRACECMD_DIR)/libtracecmd.a\nLIBTRACECMD_SHARED = $(LIBTRACECMD_DIR)/libtracecmd.so.$(LIBTRACECMD_VERSION)\nLIBTRACECMD_SHARED_VERSION := $(shell echo $(LIBTRACECMD_SHARED) | sed -e 's/\\(\\.so\\.[0-9]*\\).*/\\1/')\nLIBTRACECMD_SHARED_SO := $(shell echo $(LIBTRACECMD_SHARED) | sed -e 's/\\(\\.so\\).*/\\1/')\n\nexport LIBTRACECMD_STATIC LIBTRACECMD_SHARED\nexport LIBTRACECMD_SHARED_VERSION LIBTRACECMD_SHARED_SO\n\nLIBTRACEEVENT=libtraceevent\nLIBTRACEFS=libtracefs\n\nTEST_LIBTRACEEVENT := $(shell sh -c \"$(PKG_CONFIG) --atleast-version $(LIBTRACEEVENT_MIN_VERSION) $(LIBTRACEEVENT) > /dev/null 2>&1 && echo y\")\nTEST_LIBTRACEFS := $(shell sh -c \"$(PKG_CONFIG) --atleast-version $(LIBTRACEFS_MIN_VERSION) $(LIBTRACEFS) > /dev/null 2>&1 && echo y\")\n\nifeq (\"$(TEST_LIBTRACEEVENT)\", \"y\")\nLIBTRACEEVENT_CFLAGS := $(shell sh -c \"$(PKG_CONFIG) --cflags $(LIBTRACEEVENT)\")\nLIBTRACEEVENT_LDLAGS := $(shell sh -c \"$(PKG_CONFIG) --libs $(LIBTRACEEVENT)\")\nelse\n.PHONY: warning\nwarning:\n\t@echo \"********************************************\"\n\t@echo \"** NOTICE: libtraceevent version $(LIBTRACEEVENT_MIN_VERSION) or higher not found on system\"\n\t@echo \"**\"\n\t@echo \"** Consider installing the latest libtraceevent from your\"\n\t@echo \"** distribution, or from source:\"\n\t@echo \"**\"\n\t@echo \"**  https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/ \"\n\t@echo \"**\"\n\t@echo \"********************************************\"\nendif\n\nexport LIBTRACEEVENT_CFLAGS LIBTRACEEVENT_LDLAGS\n\nifeq (\"$(TEST_LIBTRACEFS)\", \"y\")\nLIBTRACEFS_CFLAGS := $(shell sh -c \"$(PKG_CONFIG) --cflags $(LIBTRACEFS)\")\nLIBTRACEFS_LDLAGS := $(shell sh -c \"$(PKG_CONFIG) --libs $(LIBTRACEFS)\")\nelse\n.PHONY: warning\nwarning:\n\t@echo \"********************************************\"\n\t@echo \"** NOTICE: libtracefs version $(LIBTRACEFS_MIN_VERSION) or higher not found on system\"\n\t@echo \"**\"\n\t@echo \"** Consider installing the latest libtracefs from your\"\n\t@echo \"** distribution, or from source:\"\n\t@echo \"**\"\n\t@echo \"**  https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/ \"\n\t@echo \"**\"\n\t@echo \"********************************************\"\nendif\n\nexport LIBTRACEFS_CFLAGS LIBTRACEFS_LDLAGS\n\nTRACE_LIBS = -L$(LIBTRACECMD_DIR) -ltracecmd\t\\\n\t     $(LIBTRACEEVENT_LDLAGS) $(LIBTRACEFS_LDLAGS)\n\nexport LIBS TRACE_LIBS\nexport LIBTRACECMD_DIR\nexport Q SILENT VERBOSE EXT\n\n# Include the utils\ninclude scripts/utils.mk\n\nINCLUDES = -I$(src)/include -I$(src)/../../include\nINCLUDES += -I$(src)/include/trace-cmd\nINCLUDES += -I$(src)/lib/trace-cmd/include\nINCLUDES += -I$(src)/lib/trace-cmd/include/private\nINCLUDES += -I$(src)/tracecmd/include\nINCLUDES += $(LIBTRACEEVENT_CFLAGS)\nINCLUDES += $(LIBTRACEFS_CFLAGS)\n\ninclude $(src)/features.mk\n\n# Set compile option CFLAGS if not set elsewhere\nCFLAGS ?= -g -Wall\nCPPFLAGS ?=\nLDFLAGS ?=\n\nifndef NO_VSOCK\nVSOCK_DEFINED := $(shell if (echo \"$(pound)include <linux/vm_sockets.h>\" | $(CC) -E - >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)\nelse\nVSOCK_DEFINED := 0\nendif\n\nexport VSOCK_DEFINED\nifeq ($(VSOCK_DEFINED), 1)\nCFLAGS += -DVSOCK\nendif\n\nPERF_DEFINED := $(shell if (echo \"$(pound)include <linux/perf_event.h>\" | $(CC) -E - >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)\nexport PERF_DEFINED\nifeq ($(PERF_DEFINED), 1)\nCFLAGS += -DPERF\nendif\n\nZLIB_INSTALLED := $(shell if (printf \"$(pound)include <zlib.h>\\n void main(){deflateInit(NULL, Z_BEST_COMPRESSION);}\" | $(CC) -o /dev/null -x c - -lz >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)\nifeq ($(ZLIB_INSTALLED), 1)\nexport ZLIB_INSTALLED\nZLIB_LDLAGS = -lz\nCFLAGS += -DHAVE_ZLIB\n$(info    Have zlib compression support)\nendif\n\nexport ZLIB_LDLAGS\n\nifndef NO_LIBZSTD\nTEST_LIBZSTD := $(shell sh -c \"$(PKG_CONFIG) --atleast-version 1.4.0 libzstd > /dev/null 2>&1 && echo y\")\n\nifeq (\"$(TEST_LIBZSTD)\", \"y\")\nLIBZSTD_CFLAGS := $(shell sh -c \"$(PKG_CONFIG) --cflags libzstd\")\nLIBZSTD_LDLAGS := $(shell sh -c \"$(PKG_CONFIG) --libs libzstd\")\nCFLAGS += -DHAVE_ZSTD\nZSTD_INSTALLED=1\n$(info    Have ZSTD compression support)\nelse\n$(info\t  *************************************************************)\n$(info\t  ZSTD package not found, best compression algorithm not in use)\n$(info\t  *************************************************************)\nendif\n\nexport LIBZSTD_CFLAGS LIBZSTD_LDLAGS ZSTD_INSTALLED\nendif\n\nCUNIT_INSTALLED := $(shell if (printf \"$(pound)include <CUnit/Basic.h>\\n void main(){CU_initialize_registry();}\" | $(CC) -o /dev/null -x c - -lcunit >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)\nexport CUNIT_INSTALLED\n\nexport CFLAGS\nexport INCLUDES\n\n# Required CFLAGS\noverride CFLAGS += -D_GNU_SOURCE\n\n# Make sure 32 bit stat() works on large file systems\noverride CFLAGS += -D_FILE_OFFSET_BITS=64\n\nifndef NO_PTRACE\nifneq ($(call try-cc,$(SOURCE_PTRACE),),y)\n\tNO_PTRACE = 1\n\toverride CFLAGS += -DWARN_NO_PTRACE\nendif\nendif\n\nifdef NO_PTRACE\noverride CFLAGS += -DNO_PTRACE\nendif\n\nifndef NO_AUDIT\nifneq ($(call try-cc,$(SOURCE_AUDIT),-laudit),y)\n\tNO_AUDIT = 1\n\toverride CFLAGS += -DWARN_NO_AUDIT\nendif\nendif\n\nifdef NO_AUDIT\noverride CFLAGS += -DNO_AUDIT\nelse\nLIBS += -laudit\nendif\n\n# Append required CFLAGS\noverride CFLAGS += $(INCLUDES) $(VAR_DIR)\noverride CFLAGS += $(PLUGIN_DIR_TRACECMD_SQ)\noverride CFLAGS += $(udis86-flags) $(blk-flags) $(memfd-flags)\noverride LDFLAGS += $(udis86-ldflags)\n\nCMD_TARGETS = trace-cmd $(BUILD_PYTHON)\n\n###\n#    Default we just build trace-cmd\n#\n#    If you want all libraries, then do: make libs\n###\n\nall: all_cmd plugins show_other_make\n\nall_cmd: $(CMD_TARGETS)\n\nBUILD_PREFIX := $(BUILD_OUTPUT)/build_prefix\n\n$(BUILD_PREFIX): force\n\t$(Q)$(call build_prefix,$(prefix))\n\n$(PKG_CONFIG_FILE) : ${PKG_CONFIG_SOURCE_FILE}.template $(BUILD_PREFIX) $(VERSION_FILE)\n\t$(Q) $(call do_make_pkgconfig_file,$(prefix))\n\ntrace-cmd: force $(LIBTRACECMD_STATIC) \\\n\tforce $(obj)/lib/trace-cmd/plugins/tracecmd_plugin_dir\n\t$(Q)$(MAKE) -C $(src)/tracecmd $(obj)/tracecmd/$@\n\n$(LIBTRACECMD_STATIC): force\n\t$(Q)$(MAKE) -C $(src)/lib/trace-cmd $@\n\n$(LIBTRACECMD_SHARED): force\n\t$(Q)$(MAKE) -C $(src)/lib/trace-cmd libtracecmd.so\n\nlibtracecmd.a: $(LIBTRACECMD_STATIC)\nlibtracecmd.so: $(LIBTRACECMD_SHARED)\n\nlibs: $(LIBTRACECMD_SHARED) $(PKG_CONFIG_FILE)\n\nVERSION = $(LIBTC_VERSION)\nPATCHLEVEL = $(LIBTC_PATCHLEVEL)\nEXTRAVERSION = $(LIBTC_EXTRAVERSION)\n\ndefine make_version.h\n  (echo '/* This file is automatically generated. Do not modify. */';\t\t\\\n   echo \\#define VERSION_CODE $(shell\t\t\t\t\t\t\\\n   expr $(VERSION) \\* 256 + $(PATCHLEVEL));\t\t\t\t\t\\\n   echo '#define EXTRAVERSION ' $(EXTRAVERSION);\t\t\t\t\\\n   echo '#define VERSION_STRING \"'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'\"';\t\\\n  ) > $1\nendef\n\ndefine update_version.h\n  ($(call make_version.h, $@.tmp);\t\t\\\n    if [ -r $@ ] && cmp -s $@ $@.tmp; then\t\\\n      rm -f $@.tmp;\t\t\t\t\\\n    else\t\t\t\t\t\\\n      echo '  UPDATE                 $@';\t\\\n      mv -f $@.tmp $@;\t\t\t\t\\\n    fi);\nendef\n\n$(VERSION_FILE): force\n\t$(Q)$(call update_version.h)\n\ngui: force\n\t@echo \"***************************\"\n\t@echo \"  KernelShark has moved!\"\n\t@echo \"  Please use its new home at https://git.kernel.org/pub/scm/utils/trace-cmd/kernel-shark.git/\"\n\t@echo \"***************************\"\n\ntest: force trace-cmd\nifneq ($(CUNIT_INSTALLED),1)\n\t$(error CUnit framework not installed, cannot build unit tests))\nendif\n\t$(Q)$(MAKE) -C $(src)/utest $@\n\ntest_mem: force test\n\t$(Q)$(MAKE) -C $(src)/utest $@\n\nplugins_tracecmd: force $(obj)/lib/trace-cmd/plugins/tracecmd_plugin_dir\n\t$(Q)$(MAKE) -C $(src)/lib/trace-cmd/plugins\n\nplugins: plugins_tracecmd\n\n$(obj)/lib/trace-cmd/plugins/tracecmd_plugin_dir: force\n\t$(Q)$(MAKE) -C $(src)/lib/trace-cmd/plugins $@\n\nshow_other_make:\n\t@echo \"Note: to build man pages, type \\\"make doc\\\"\"\n\t@echo \"      to build unit tests, type \\\"make test\\\"\"\n\nPHONY += show_other_make\n\ndefine find_tag_files\n\tfind . -name '\\.pc' -prune -o -name '*\\.[ch]' -print -o -name '*\\.[ch]pp' \\\n\t\t! -name '\\.#' -print\nendef\n\ntags:\tforce\n\t$(RM) tags\n\t$(call find_tag_files) | xargs ctags --extra=+f --c-kinds=+px\n\nTAGS:\tforce\n\t$(RM) TAGS\n\t$(call find_tag_files) | xargs etags\n\ncscope: force\n\t$(RM) cscope*\n\t$(call find_tag_files) > cscope.files\n\tcscope -b -q -f cscope.out\n\ninstall_plugins_tracecmd: force\n\t$(Q)$(MAKE) -C $(src)/lib/trace-cmd/plugins install_plugins\n\ninstall_plugins: install_plugins_tracecmd\n\ninstall_python: force\n\t$(Q)$(MAKE) -C $(src)/python $@\n\ninstall_bash_completion: force\n\t$(Q)$(call do_install_data,$(src)/tracecmd/trace-cmd.bash,$(completion_dir))\n\ninstall_cmd: all_cmd install_plugins install_python install_bash_completion\n\t$(Q)$(call do_install,$(obj)/tracecmd/trace-cmd,$(bindir_SQ))\n\ninstall: install_cmd\n\t@echo \"Note: to install man pages, type \\\"make install_doc\\\"\"\n\ninstall_gui: force\n\t@echo \"Nothing to do here.\"\n\t@echo \" Have you tried https://git.kernel.org/pub/scm/utils/trace-cmd/kernel-shark.git/\"\n\ninstall_libs: libs\n\t$(Q)$(MAKE) -C $(src)/lib/trace-cmd/ $@\n\ndoc: check_doc\n\t$(MAKE) -C $(src)/Documentation all\n\ndoc_clean:\n\t$(MAKE) -C $(src)/Documentation clean\n\ninstall_doc:\n\t$(MAKE) -C $(src)/Documentation install\n\ncheck_doc: force\n\t$(Q)$(src)/check-manpages.sh $(src)/Documentation/libtracecmd\n\nclean: clean_meson\n\t$(RM) *.o *~ *.a *.so .*.d\n\t$(RM) tags TAGS cscope* $(PKG_CONFIG_SOURCE_FILE) $(VERSION_FILE)\n\t$(MAKE) -C $(src)/lib/trace-cmd clean\n\t$(MAKE) -C $(src)/lib/trace-cmd/plugins clean\n\t$(MAKE) -C $(src)/utest clean\n\t$(MAKE) -C $(src)/python clean\n\t$(MAKE) -C $(src)/tracecmd clean\n\ndefine build_uninstall_script\n\t$(Q)mkdir $(BUILD_OUTPUT)/tmp_build\n\t$(Q)$(MAKE) -C $(src) DESTDIR=$(BUILD_OUTPUT)/tmp_build O=$(BUILD_OUTPUT) $1 > /dev/null\n\t$(Q)find $(BUILD_OUTPUT)/tmp_build ! -type d -printf \"%P\\n\" > $(BUILD_OUTPUT)/build_$2\n\t$(Q)$(RM) -rf $(BUILD_OUTPUT)/tmp_build\nendef\n\nbuild_uninstall: $(BUILD_PREFIX)\n\t$(call build_uninstall_script,install,uninstall)\n\n$(BUILD_OUTPUT)/build_uninstall: build_uninstall\n\nbuild_libs_uninstall: $(BUILD_PREFIX)\n\t$(call build_uninstall_script,install_libs,libs_uninstall)\n\n$(BUILD_OUTPUT)/build_libs_uninstall: build_libs_uninstall\n\ndefine uninstall_file\n\tif [ -f $(DESTDIR)/$1 -o -h $(DESTDIR)/$1 ]; then \\\n\t\t$(call print_uninstall,$(DESTDIR)/$1)$(RM) $(DESTDIR)/$1; \\\n\tfi;\nendef\n\nuninstall: $(BUILD_OUTPUT)/build_uninstall\n\t@$(foreach file,$(shell cat $(BUILD_OUTPUT)/build_uninstall),$(call uninstall_file,$(file)))\n\nuninstall_libs: $(BUILD_OUTPUT)/build_libs_uninstall\n\t@$(foreach file,$(shell cat $(BUILD_OUTPUT)/build_libs_uninstall),$(call uninstall_file,$(file)))\n\n##### PYTHON STUFF #####\n\nreport_noswig: force\n\t$(Q)echo\n\t$(Q)echo \"    NO_PYTHON forced: swig not installed, not compiling python plugins\"\n\t$(Q)echo\n\nreport_nopythondev: force\n\t$(Q)echo\n\t$(Q)echo \"    python-dev is not installed, not compiling python plugins\"\n\t$(Q)echo\n\nifndef NO_PYTHON\nPYTHON_INCLUDES = `$(PKG_CONFIG) --cflags $(PYTHON_PKGCONFIG_VERS)`\nPYTHON_LDFLAGS = `$(PKG_CONFIG) --libs $(PYTHON_PKGCONFIG_VERS)` \\\n\t\t$(shell $(PYTHON_VERS)-config --ldflags)\nPYGTK_CFLAGS = `$(PKG_CONFIG) --cflags pygtk-2.0`\nelse\nPYTHON_INCLUDES =\nPYTHON_LDFLAGS =\nPYGTK_CFLAGS =\nendif\n\nexport PYTHON_INCLUDES\nexport PYTHON_LDFLAGS\nexport PYGTK_CFLAGS\n\nctracecmd.so: force $(LIBTRACECMD_STATIC)\n\t$(Q)$(MAKE) -C $(src)/python $@\n\nPHONY += python\npython: $(PYTHON)\n\nmeson:\n\t$(MAKE) -f Makefile.meson\n\nmeson_install:\n\t$(MAKE) -f Makefile.meson install\n\nmeson_docs:\n\t$(MAKE) -f Makefile.meson docs\n\nPHONY += clean_meson\nclean_meson:\n\t$(Q)$(MAKE) -f Makefile.meson $@\n\ndist:\n\tgit archive --format=tar --prefix=trace-cmd-$(TRACECMD_VERSION)/ HEAD \\\n\t\t> ../trace-cmd-$(TRACECMD_VERSION).tar\n\tcat ../trace-cmd-$(TRACECMD_VERSION).tar | \\\n\t\tbzip2 -c9 > ../trace-cmd-$(TRACECMD_VERSION).tar.bz2\n\tcat ../trace-cmd-$(TRACECMD_VERSION).tar | \\\n\t\txz -e -c8 > ../trace-cmd-$(TRACECMD_VERSION).tar.xz\n\nPHONY += force\nforce:\n\n# Declare the contents of the .PHONY variable as phony.  We keep that\n# information in a variable so we can use it in if_changed and friends.\n.PHONY: $(PHONY)\n"
  },
  {
    "path": "Makefile.meson",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\nundefine CFLAGS\n\n# Makefiles suck: This macro sets a default value of $(2) for the\n# variable named by $(1), unless the variable has been set by\n# environment or command line. This is necessary for CC and AR\n# because make sets default values, so the simpler ?= approach\n# won't work as expected.\ndefine allow-override\n  $(if $(or $(findstring environment,$(origin $(1))),\\\n            $(findstring command line,$(origin $(1)))),,\\\n    $(eval $(1) = $(2)))\nendef\n\n$(call allow-override,MESON,meson)\n$(call allow-override,MESON_BUILD_DIR,build)\n\n\nall: compile\n\nPHONY += compile\ncompile: $(MESON_BUILD_DIR) force\n\t$(MESON) compile -C $(MESON_BUILD_DIR)\n\n$(MESON_BUILD_DIR):\n\t$(MESON) setup --prefix=$(prefix) $(MESON_BUILD_DIR)\n\ninstall: compile\n\t$(MESON) install -C $(MESON_BUILD_DIR)\n\ndocs: $(MESON_BUILD_DIR)\n\t$(MESON) compile -C build docs\n\nPHONY += clean_meson\nclean_meson:\n\t$(Q)$(RM) -rf $(MESON_BUILD_DIR)\n\nPHONY += force\nforce:\n"
  },
  {
    "path": "PACKAGING",
    "content": "The libtracefs and libtraceevent packages are required for trace-cmd\nand libtracecmd.so\n\nIn order to create a package directory with libtraceevent, libtracefs\nand libtracecmd and trace-cmd, you can follow these steps:\n\n git clone git://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git\n git clone git://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git\n git clone git://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git\n\n cd libtraceevent\n INSTALL_PATH=/tmp/install ../trace-cmd/make-trace-cmd.sh install\n\n cd ../libtracefs\n INSTALL_PATH=/tmp/install ../trace-cmd/make-trace-cmd.sh install\n\n cd ../trace-cmd\n INSTALL_PATH=/tmp/install ./make-trace-cmd.sh install install_libs\n\n cd /tmp/install\n tar cvjf /tmp/trace-cmd-files.tar.bz2 .\n\nAnd then the tarball of /tmp/trace-cmd-files.tar.bz2 will can be extracted\non another machine at the root directory, and trace-cmd will be installed there.\n\nNote, to define a prefix, add a PREFIX variable before calling make-trace-cmd.sh\n\n For example:\n\n  PREFIX=/usr/local INSTALL_PATH=/tmp/install ./make-trace-cmd.sh install\n"
  },
  {
    "path": "README",
    "content": "\n\n  For more information on contributing please see: https://www.trace-cmd.org\n\nNote: The official repositiory for trace-cmd and KernelShark is here:\n\n git://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git\n\nFor bug reports and issues, please file it here:\n\n https://bugzilla.kernel.org/buglist.cgi?component=Trace-cmd%2FKernelshark&product=Tools&resolution=---\n\nThese files make up the code that create the trace-cmd programs.\nThis includes the GUI interface application kernelshark as well\nas trace-graph and trace-view.\n\nThese files also make up the code to create the libtracecmd library.\n\nThe applications are licensed under the GNU General Public License 2.0\n(see COPYING) and the libraries are licensed under the GNU\nLesser General Public License 2.1 (See COPYING.LIB).\n\nBUILDING:\n\nIn order to install build dependencies on Debian / Ubuntu do the following:\n    sudo apt-get install build-essential git pkg-config -y\n    sudo apt-get install libtracefs-dev libtraceevent-dev -y\n\nIn order to install build dependencies on Fedora, as root do the following:\n    dnf install gcc make git pkg-config -y\n    dnf install libtracefs-devel libtraceevent-devel -y\n\nIn case your distribution does not have the required libtracefs and\nlibtraceevent libraries, build and install them manually:\n\n    git clone https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/\n    cd libtraceevent\n    make\n    sudo make install\n\n    git clone https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/\n    cd libtracefs\n    make\n    sudo make install\n\nTo make trace-cmd\n    make\n\nTo make the gui\n    make gui\n\nINSTALL:\n\nTo install trace-cmd\n    make install\n\nTo install the gui\n    make install_gui\n\nTo install libtracecmd libraries\n    make install_libs\n\nNote: The default install is relative to /usr/local\n    The default install directory is /usr/local/bin\n    The default plugin directory is /usr/local/lib/trace-cmd/plugins\n\nTo change the default, you can set 'prefix', eg\nmkdir $HOME/test-trace\nmake prefix=$HOME/test-trace\nmake prefix=$HOME/test-trace install\n\n"
  },
  {
    "path": "check-manpages.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: LGPL-2.1\n# Copyright (C) 2022, Google Inc, Steven Rostedt <rostedt@goodmis.org>\n#\n# This checks if any function is listed in a man page that is not listed\n# in the main man page.\n\nif [ $# -lt 1 ]; then\n\techo \"usage: check-manpages man-page-path\"\n\texit 1\nfi\n\ncd $1\n\nMAIN=libtracecmd\nMAIN_FILE=${MAIN}.txt\n\nPROCESSED=\"\"\n\n# Ignore man pages that do not contain functions\nIGNORE=\"\"\n\nfor man in ${MAIN}-*.txt; do\n\n\tfor a in `sed -ne '/^NAME/,/^SYNOP/{/^[a-z]/{s/, *$//;s/,/\\n/g;s/ //g;s/-.*$/-/;/-/{s/-//p;q};p}}' $man`; do\n\t\tif [ \"${PROCESSED/:${a} /}\" != \"${PROCESSED}\" ]; then\n\t\t\tP=\"${PROCESSED/:${a} */}\"\n\t\t\techo \"Found ${a} in ${man} and in ${P/* /}\"\n\t\tfi\n\t\tPROCESSED=\"${man}:${a} ${PROCESSED}\"\n\t\tif [ \"${IGNORE/$man/}\" != \"${IGNORE}\" ]; then\n\t\t\tcontinue\n\t\tfi\n\t\tif ! grep -q '\\*'${a}'\\*' $MAIN_FILE; then\n\t\t\tif [ \"$last\" == \"\" ]; then\n\t\t\t\techo\n\t\t\tfi\n\t\t\tif [ \"$last\" != \"$man\" ]; then\n\t\t\t\techo \"Missing functions from $MAIN_FILE that are in $man\"\n\t\t\t\tlast=$man\n\t\t\tfi\n\t\t\techo \"   ${a}\"\n\t\tfi\n\tdone\ndone\n\nDEPRECATED=\"\"\n\nsed -ne 's/^[a-z].*[ \\*]\\([a-z_][a-z_]*\\)(.*/\\1/p' -e 's/^\\([a-z_][a-z_]*\\)(.*/\\1/p' ../../include/trace-cmd/trace-cmd.h | while read f; do\n\tif ! grep -q '\\*'${f}'\\*' $MAIN_FILE; then\n\t\tif [ \"${DEPRECATED/\\*$f\\*/}\" != \"${DEPRECATED}\" ]; then\n\t\t\tcontinue;\n\t\tfi\n\t\tif [ \"$last\" == \"\" ]; then\n\t\t\techo\n\t\t\techo \"Missing functions from $MAIN_FILE that are in tracefs.h\"\n\t\t\tlast=$f\n\t\tfi\n\t\techo \"   ${f}\"\n\tfi\ndone\n"
  },
  {
    "path": "features.mk",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\n# taken from perf which was based on Linux Kbuild\n# try-cc\n# Usage: option = $(call try-cc, source-to-build, cc-options)\ntry-cc = $(shell sh -c\t\t\t\t\t\t\t\\\n\t'TMP=\"$(BUILD_OUTPUT)$(TMPOUT).$$$$\";\t\t\t\t\t\t\\\n\techo \"$(1)\" |\t\t\t\t\t\t\t\\\n\t$(CC) -x c - $(2) -o \"$$TMP\" > /dev/null 2>&1 && echo y;\t\\\n\trm -f \"$$TMP\"')\n\ndefine SOURCE_PTRACE\n#include <stdio.h>\n#include <sys/ptrace.h>\n\nint main (void)\n{\n\tint ret;\n\tret = ptrace(PTRACE_ATTACH, 0, NULL, 0);\n\tptrace(PTRACE_TRACEME, 0, NULL, 0);\n\tptrace(PTRACE_GETSIGINFO, 0, NULL, NULL);\n\tptrace(PTRACE_GETEVENTMSG, 0, NULL, NULL);\n\tptrace(PTRACE_SETOPTIONS, NULL, NULL,\n\t\t       PTRACE_O_TRACEFORK |\n\t\t       PTRACE_O_TRACEVFORK |\n\t\t       PTRACE_O_TRACECLONE |\n\t\t       PTRACE_O_TRACEEXIT);\n\tptrace(PTRACE_CONT, NULL, NULL, 0);\n\tptrace(PTRACE_DETACH, 0, NULL, NULL);\n\tptrace(PTRACE_SETOPTIONS, 0, NULL,\n\t       PTRACE_O_TRACEFORK |\n\t       PTRACE_O_TRACEVFORK |\n\t       PTRACE_O_TRACECLONE |\n\t       PTRACE_O_TRACEEXIT);\n\treturn ret;\n}\nendef\n\ndefine SOURCE_AUDIT\n#include <stdio.h>\n#include <libaudit.h>\n\nint main (void)\n{\n\tchar *name;\n\tint ret;\n\tret = audit_detect_machine();\n\tif (ret < 0)\n\t\treturn ret;\n\tname = audit_syscall_to_name(1, ret);\n\tif (!name)\n\t\treturn -1;\n\treturn ret;\n}\nendef\n"
  },
  {
    "path": "include/linux/time64.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 */\n\n#ifndef _TOOLS_LINUX_TIME64_H\n#define _TOOLS_LINUX_TIME64_H\n\n#define MSEC_PER_SEC\t1000L\n#define USEC_PER_MSEC\t1000L\n#define NSEC_PER_USEC\t1000L\n#define NSEC_PER_MSEC\t1000000L\n#define USEC_PER_SEC\t1000000L\n#define NSEC_PER_SEC\t1000000000L\n#define FSEC_PER_SEC\t1000000000000000LL\n\n#endif /* _LINUX_TIME64_H */\n"
  },
  {
    "path": "include/trace-cmd/trace-cmd.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#ifndef _TRACE_CMD_H\n#define _TRACE_CMD_H\n\n#include \"event-parse.h\"\n#include \"tracefs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct tracecmd_input;\n\nenum tracecmd_open_flags {\n\tTRACECMD_FL_LOAD_NO_PLUGINS\t\t= 1 << 0, /* Do not load plugins */\n\tTRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS\t= 1 << 1, /* Do not load system plugins */\n};\n\nenum tracecmd_section_flags {\n\tTRACECMD_SEC_FL_COMPRESS\t= 1 << 0, /* the section is compressed */\n};\n\nstruct tracecmd_input *tracecmd_open_head(const char *file, int flags);\nstruct tracecmd_input *tracecmd_open(const char *file, int flags);\nstruct tracecmd_input *tracecmd_open_fd(int fd, int flags);\n\nvoid tracecmd_close(struct tracecmd_input *handle);\n\nint tracecmd_init_data(struct tracecmd_input *handle);\nstruct tep_record *\ntracecmd_read_cpu_first(struct tracecmd_input *handle, int cpu);\nstruct tep_record *\ntracecmd_read_data(struct tracecmd_input *handle, int cpu);\nstruct tep_record *\ntracecmd_read_at(struct tracecmd_input *handle, unsigned long long offset,\n\t\t int *cpu);\nvoid tracecmd_free_record(struct tep_record *record);\n\nstruct tep_handle *tracecmd_get_tep(struct tracecmd_input *handle);\nunsigned long long tracecmd_get_traceid(struct tracecmd_input *handle);\nint tracecmd_get_guest_cpumap(struct tracecmd_input *handle,\n\t\t\t      unsigned long long trace_id,\n\t\t\t      const char **name,\n\t\t\t      int *vcpu_count, const int **cpu_pid);\nunsigned long long tracecmd_get_first_ts(struct tracecmd_input *handle);\nvoid tracecmd_add_ts_offset(struct tracecmd_input *handle, long long offset);\nint tracecmd_get_tsc2nsec(struct tracecmd_input *handle,\n\t\t\t  int *mult, int *shift, unsigned long long *offset);\nint tracecmd_buffer_instances(struct tracecmd_input *handle);\nconst char *tracecmd_buffer_instance_name(struct tracecmd_input *handle, int indx);\nstruct tracecmd_input *tracecmd_buffer_instance_handle(struct tracecmd_input *handle, int indx);\n\nvoid tracecmd_set_private(struct tracecmd_input *handle, void *data);\nvoid *tracecmd_get_private(struct tracecmd_input *handle);\n\nint tracecmd_follow_event(struct tracecmd_input *handle,\n\t\t\t  const char *system, const char *event_name,\n\t\t\t  int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t  struct tep_event *,\n\t\t\t\t\t  struct tep_record *,\n\t\t\t\t\t  int, void *),\n\t\t\t  void *callback_data);\n\nint tracecmd_follow_missed_events(struct tracecmd_input *handle,\n\t\t\t\t  int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t\t  struct tep_event *,\n\t\t\t\t\t\t  struct tep_record *,\n\t\t\t\t\t\t  int, void *),\n\t\t\t\t  void *callback_data);\n\nint tracecmd_iterate_reset(struct tracecmd_input *handle);\n\nint tracecmd_iterate_events(struct tracecmd_input *handle,\n\t\t\t    cpu_set_t *cpus, int cpu_size,\n\t\t\t    int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t    struct tep_record *,\n\t\t\t\t\t    int, void *),\n\t\t\t    void *callback_data);\nint tracecmd_iterate_events_multi(struct tracecmd_input **handles,\n\t\t\t\t  int nr_handles,\n\t\t\t\t  int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t\t  struct tep_record *,\n\t\t\t\t\t\t  int, void *),\n\t\t\t\t  void *callback_data);\nint tracecmd_iterate_events_reverse(struct tracecmd_input *handle,\n\t\t\t\t    cpu_set_t *cpus, int cpu_size,\n\t\t\t\t    int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t\t    struct tep_record *,\n\t\t\t\t\t\t    int, void *),\n\t\t\t\t    void *callback_data, bool cont);\n\nvoid tracecmd_set_loglevel(enum tep_loglevel level);\n\nenum tracecmd_filters {\n\tTRACECMD_FILTER_NONE\t\t= TEP_ERRNO__NO_FILTER,\n\tTRACECMD_FILTER_NOT_FOUND\t= TEP_ERRNO__FILTER_NOT_FOUND,\n\tTRACECMD_FILTER_MISS\t\t= TEP_ERRNO__FILTER_MISS,\n\tTRACECMD_FILTER_MATCH\t\t= TEP_ERRNO__FILTER_MATCH,\n};\n\nstruct tracecmd_filter;\nstruct tracecmd_filter *tracecmd_filter_add(struct tracecmd_input *handle,\n\t\t\t\t\t    const char *filter_str, bool neg);\n\nstruct tracecmd_cpu_map;\nint tracecmd_map_vcpus(struct tracecmd_input **handles, int nr_handles);\nstruct tracecmd_cpu_map *tracecmd_get_cpu_map(struct tracecmd_input *handle, int cpu);\nstruct tracecmd_cpu_map *tracecmd_map_find_by_host_pid(struct tracecmd_input *handle,\n\t\t\t\t\t\t      int host_pid);\nstruct tracecmd_input *tracecmd_map_get_guest(struct tracecmd_cpu_map *map);\nint tracecmd_map_get_host_pid(struct tracecmd_cpu_map *map);\nvoid tracecmd_map_set_private(struct tracecmd_cpu_map *map, void *priv);\nvoid *tracecmd_map_get_private(struct tracecmd_cpu_map *map);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _TRACE_CMD_H */\n"
  },
  {
    "path": "include/version.h",
    "content": "#ifndef _VERSION_H\n#define _VERSION_H\n\n#define VERSION(a, b) (((a) << 8) + (b))\n\n#ifdef BUILDGUI\n#include \"ks_version.h\"\n#else\n#include \"tc_version.h\"\n#endif\n\n#endif /* _VERSION_H */\n"
  },
  {
    "path": "lib/meson.build",
    "content": "# SPDX-License-Identifier: LGPL-2.1\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nproject(\n    'libtracecmd', ['c'],\n    meson_version: '>= 0.50.0',\n    license: 'GPL-2.0',\n    version: '1.5.5',\n    default_options: [\n        'c_std=gnu99',\n        'buildtype=debug',\n        'default_library=both',\n        'prefix=/usr/local',\n        'warning_level=1'])\n\ncc = meson.get_compiler('c')\n\nprefixdir = get_option('prefix')\nmandir    = join_paths(prefixdir, get_option('mandir'))\nhtmldir   = join_paths(prefixdir, get_option('htmldir'))\n\nlibtracecmd_standalone_build = true\n\nlibrary_version = meson.project_version()\n\nconf = configuration_data()\n\nlibtraceevent_dep = dependency('libtraceevent', version: '>= 1.5.0', required: true)\nlibtracefs_dep = dependency('libtracefs', version: '>= 1.6.0', required: true)\n\nthreads_dep = dependency('threads', required: true)\ndl_dep = cc.find_library('dl', required : false)\n\nzlib_dep = dependency('zlib', required: false)\nconf.set('HAVE_ZLIB', zlib_dep.found(), description: 'Is zlib avialable?')\n\nlibzstd_dep = dependency('libzstd', version: '>= 1.4.0', required: false)\nconf.set('HAVE_ZSTD', libzstd_dep.found(), description: 'Is libzstd available?')\n\ncunit_dep = dependency('cunit', required : false)\n\nvsock_defined = get_option('vsock') and cc.has_header('linux/vm_sockets.h')\nconf.set('VSOCK', vsock_defined, description: 'Is vsock available?')\n\nperf_defined = cc.has_header('linux/perf_event.h')\nconf.set('PERF', perf_defined, description: 'Is perf available?')\n\nhave_ptrace = get_option('ptrace') and cc.compiles(\n    '''\n    #include <stdio.h>\n    #include <sys/ptrace.h>\n\n    int main (void)\n    {\n            int ret;\n            ret = ptrace(PTRACE_ATTACH, 0, NULL, 0);\n            ptrace(PTRACE_TRACEME, 0, NULL, 0);\n            ptrace(PTRACE_GETSIGINFO, 0, NULL, NULL);\n            ptrace(PTRACE_GETEVENTMSG, 0, NULL, NULL);\n            ptrace(PTRACE_SETOPTIONS, NULL, NULL,\n                           PTRACE_O_TRACEFORK |\n                           PTRACE_O_TRACEVFORK |\n                           PTRACE_O_TRACECLONE |\n                           PTRACE_O_TRACEEXIT);\n            ptrace(PTRACE_CONT, NULL, NULL, 0);\n            ptrace(PTRACE_DETACH, 0, NULL, NULL);\n            ptrace(PTRACE_SETOPTIONS, 0, NULL,\n                   PTRACE_O_TRACEFORK |\n                   PTRACE_O_TRACEVFORK |\n                   PTRACE_O_TRACECLONE |\n                   PTRACE_O_TRACEEXIT);\n            return ret;\n    }\n    ''',\n    name: 'ptrace')\nif not have_ptrace\n    conf.set10('NO_PTRACE', true, description: 'Is ptrace missing?')\n    conf.set('WARN_NO_PTRACE', true, description: 'Issue no ptrace warning?')\nendif\n\naudit_dep = dependency('audit', required: false)\nif not audit_dep.found()\n    conf.set10('NO_AUDIT', true, description: 'Is audit missing?')\n    conf.set('WARN_NO_AUDIT', true, description: 'Issue no audit warning?')\nendif\n\nadd_project_arguments(\n    [\n        '-D_GNU_SOURCE',\n        '-include', 'trace-cmd/include/private/config.h',\n    ],\n    language : 'c')\n\nlibtracecmd_ext_incdir = include_directories(\n    [\n        '../include',\n        '../include/trace-cmd',\n        '../tracecmd/include'\n    ])\n\nsubdir('trace-cmd/include')\nsubdir('trace-cmd/include/private')\nsubdir('trace-cmd')\nif libtracecmd_standalone_build\n    subdir('Documentation/libtracecmd')\n\n    custom_target(\n        'docs',\n        output: 'docs',\n        depends: [html, man],\n        command: ['echo'])\nendif\n\ninstall_headers(\n    '../include/trace-cmd/trace-cmd.h',\n     subdir: 'trace-cmd')\n"
  },
  {
    "path": "lib/meson_options.txt",
    "content": "# -*- mode: meson -*-\n# SPDX-License-Identifier: LGPL-2.1\n\noption('vsock', type : 'boolean', value : true,\n       description : 'build with vsock support')\noption('ptrace', type : 'boolean', value : true,\n       description : 'build with ptrace support')\noption('htmldir', type : 'string', value : 'share/doc/libtracecmd-doc',\n       description : 'directory for HTML documentation')\noption('asciidoctor', type : 'boolean', value: false,\n       description : 'use asciidoctor instead of asciidoc')\noption('docbook-xls-172', type : 'boolean', value : false,\n       description : 'enable docbook XLS 172 workaround')\noption('asciidoc-no-roff', type : 'boolean', value : false,\n       description : 'enable no roff workaround')\noption('man-bold-literal', type : 'boolean', value : false,\n       description : 'enable bold literals')\noption('docbook-suppress-sp', type : 'boolean', value : false,\n       description : 'docbook suppress sp')\n"
  },
  {
    "path": "lib/trace-cmd/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\ninclude $(src)/scripts/utils.mk\n\nbdir:=$(obj)/lib/trace-cmd\nldir:=$(src)/lib/trace-cmd\n\nDEFAULT_TARGET = $(LIBTRACECMD_STATIC)\n\nOBJS =\nOBJS += trace-hash.o\nOBJS += trace-rbtree.o\nOBJS += trace-hooks.o\nOBJS += trace-input.o\nOBJS += trace-output.o\nOBJS += trace-recorder.o\nOBJS += trace-util.o\nOBJS += trace-filter-hash.o\nOBJS += trace-filter.o\nOBJS += trace-msg.o\nOBJS += trace-plugin.o\nOBJS += trace-maps.o\nifeq ($(PERF_DEFINED), 1)\nOBJS += trace-perf.o\nendif\nOBJS += trace-timesync.o\nOBJS += trace-timesync-ptp.o\nifeq ($(VSOCK_DEFINED), 1)\nOBJS += trace-timesync-kvm.o\nendif\nOBJS += trace-compress.o\nifeq ($(ZLIB_INSTALLED), 1)\nOBJS += trace-compress-zlib.o\nendif\nifeq ($(ZSTD_INSTALLED), 1)\nOBJS += trace-compress-zstd.o\nendif\n\n# Additional util objects\nOBJS += trace-blk-hack.o\nOBJS += trace-ftrace.o\n\nOBJS := $(OBJS:%.o=$(bdir)/%.o)\nDEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)\n\nall: $(DEFAULT_TARGET)\n\n$(bdir):\n\t@mkdir -p $(bdir)\n\n$(OBJS): | $(bdir)\n$(DEPS): | $(bdir)\n\n$(LIBTRACECMD_STATIC): $(OBJS)\n\t$(Q)$(call do_build_static_lib)\n\nLPTHREAD ?= -lpthread\n\nLIBS = $(LIBTRACEEVENT_LDLAGS) $(LIBTRACEFS_LDLAGS) $(ZLIB_LDLAGS) $(LIBZSTD_LDLAGS) $(LPTHREAD)\n\n$(LIBTRACECMD_SHARED_VERSION): $(LIBTRACECMD_SHARED)\n\t@ln -sf $(<F) $@\n\n$(LIBTRACECMD_SHARED_SO): $(LIBTRACECMD_SHARED_VERSION)\n\t@ln -sf $(<F) $@\n\nlibtracecmd.so: force $(LIBTRACECMD_SHARED_SO)\n\n$(LIBTRACECMD_SHARED): $(OBJS)\n\t$(Q)$(call do_compile_shared_library,$(notdir $(LIBTRACECMD_SHARED_VERSION)))\n\n$(bdir)/%.o: %.c\n\t$(Q)$(call do_fpic_compile)\n\n$(DEPS): $(bdir)/.%.d: %.c\n\t$(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@\n\n$(OBJS): $(bdir)/%.o : $(bdir)/.%.d\n\nifeq (\"$(DESTDIR)\", \"\")\n# If DESTDIR is not defined, then test if after installing the library\n# and running ldconfig, if the library is visible by ld.so.\n# If not, add the path to /etc/ld.so.conf.d/trace.conf and run ldconfig again.\ndefine install_ld_config\n\tif $(LDCONFIG); then \\\n\t\tif ! grep -q \"^$(libdir)$$\" $(LD_SO_CONF_PATH)/* ; then \\\n\t\t\techo here;\\\n\t\t\t$(CC) -o $(bdir)/test $(ldir)/test.c -I $(includedir_SQ) \\\n\t\t\t\t-L $(libdir_SQ) -ltracecmd &> /dev/null; \\\n\t\t\tif ! $(bdir)/test &> /dev/null; then \\\n\t\t\t\t$(call print_install,trace.conf,$(LD_SO_CONF_PATH)) \\\n\t\t\t\techo $(libdir_SQ) >> $(LD_SO_CONF_PATH)/trace.conf; \\\n\t\t\t\t$(LDCONFIG); \\\n\t\t\tfi; \\\n\t\t\t$(RM) $(bdir)/test; \\\n\t\tfi; \\\n\tfi\nendef\nelse\n# If installing to a location for another machine or package, do not bother\n# with running ldconfig.\ndefine install_ld_config\nendef\nendif # DESTDIR = \"\"\n\ninstall_pkgconfig: $(PKG_CONFIG_FILE)\n\t$(Q)$(call do_install_pkgconfig_file,$(prefix))\n\ninstall_libs: install_pkgconfig\n\t$(Q)$(call do_install,$(LIBTRACECMD_SHARED),$(libdir_SQ))\n\t$(Q)$(call print_install,$(LIBTRACECMD_SHARED_VERSION),$(DESTDIR)$(libdir_SQ))\n\t$(Q)cp -fpR $(LIBTRACECMD_SHARED_VERSION) $(DESTDIR)$(libdir_SQ)\n\t$(Q)$(call print_install,$(LIBTRACECMD_SHARED_SO),$(DESTDIR)$(libdir_SQ))\n\t$(Q)cp -fpR $(LIBTRACECMD_SHARED_SO) $(DESTDIR)$(libdir_SQ)\n\t$(Q)$(call do_install,$(src)/include/trace-cmd/trace-cmd.h,$(includedir_SQ)/trace-cmd,644)\n\t$(Q)$(call install_ld_config)\n\ndep_includes := $(wildcard $(DEPS))\n\nifneq ($(dep_includes),)\n  include $(dep_includes)\nendif\n\nclean:\n\t$(RM) $(bdir)/*.a $(bdir)/*.so $(bdir)/*.so.* $(bdir)/*.o $(bdir)/.*.d\n\n.PHONY: clean\n\nPHONY += force\nforce:\n"
  },
  {
    "path": "lib/trace-cmd/include/meson.build",
    "content": "# SPDX-License-Identifier: LGPL-2.1\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nlibtracecmd_incdir = include_directories(['.'])\n"
  },
  {
    "path": "lib/trace-cmd/include/private/meson.build",
    "content": "# SPDX-License-Identifier: LGPL-2.1\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nconfig_h = configure_file(\n    output: 'config.h',\n    configuration: conf\n)\n\nlibtracecmd_private_incdir = include_directories(['.'])\n\nconfig_dep = declare_dependency(\n    include_directories : libtracecmd_private_incdir,\n    sources: config_h)\n"
  },
  {
    "path": "lib/trace-cmd/include/private/trace-cmd-private-python.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Private interface exposed to the python module. See python/ctracecmd.i and\n * python/tracecmd.py.\n */\n#ifndef _TRACE_CMD_PRIVATE_PYTHON_H\n#define _TRACE_CMD_PRIVATE_PYTHON_H\n\nint tracecmd_long_size(struct tracecmd_input *handle);\nint tracecmd_cpus(struct tracecmd_input *handle);\n\nstruct tep_record *\ntracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu);\n\nstruct tep_record *\ntracecmd_peek_data(struct tracecmd_input *handle, int cpu);\n\nstatic inline struct tep_record *\ntracecmd_peek_data_ref(struct tracecmd_input *handle, int cpu)\n{\n\tstruct tep_record *rec = tracecmd_peek_data(handle, cpu);\n\tif (rec)\n\t\trec->ref_count++;\n\treturn rec;\n}\n\n#endif /* _TRACE_CMD_PRIVATE_PYTHON_H */\n"
  },
  {
    "path": "lib/trace-cmd/include/private/trace-cmd-private.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#ifndef _TRACE_CMD_PRIVATE_H\n#define _TRACE_CMD_PRIVATE_H\n\n#include <fcntl.h> /* for iovec */\n#include <sys/types.h>\n#include \"event-parse.h\"\n#include \"trace-cmd/trace-cmd.h\"\n#include \"trace-cmd-private-python.h\"\n\n#define __packed __attribute__((packed))\n#define __hidden __attribute__((visibility (\"hidden\")))\n\n#define TRACECMD_MAGIC { 23, 8, 68 }\n\n#define ARRAY_SIZE(_a) (sizeof(_a) / sizeof((_a)[0]))\n#define __weak __attribute__((weak))\n#define __noreturn __attribute__((noreturn))\n\n#define TRACECMD_ERR_MSK\t((unsigned long)(-1) & ~((1UL << 14) - 1))\n#define TRACECMD_ISERR(ptr)\t((unsigned long)(ptr) > TRACECMD_ERR_MSK)\n#define TRACECMD_ERROR(ret)\t((void *)((unsigned long)(ret) | TRACECMD_ERR_MSK))\n#define TRACECMD_PTR2ERR(ptr)\t((unisgned long)(ptr) & ~TRACECMD_ERR_MSK)\n\n#define TSCNSEC_CLOCK\t\"tsc2nsec\"\n\nstruct tep_plugin_list *tcmd_load_plugins(struct tep_handle *tep, int flags);\n\nint *tracecmd_add_id(int *list, int id, int len);\n\n#define FILE_VERSION_MIN\t\t6\n#define FILE_VERSION_MAX\t\t7\n\n#define FILE_VERSION_SECTIONS\t\t7\n#define FILE_VERSION_COMPRESSION\t7\n\nenum {\n\tRINGBUF_TYPE_PADDING\t\t= 29,\n\tRINGBUF_TYPE_TIME_EXTEND\t= 30,\n\tRINGBUF_TYPE_TIME_STAMP\t\t= 31,\n};\n\n/* Can be overridden */\nvoid tracecmd_debug(const char *fmt, ...);\n\nvoid tracecmd_record_ref(struct tep_record *record);\n\nvoid tracecmd_set_debug(bool set_debug);\nbool tracecmd_get_debug(void);\n\nvoid tracecmd_set_notimeout(bool set_notimeout);\nbool tracecmd_get_notimeout(void);\n\nbool tracecmd_is_version_supported(unsigned int version);\nint tracecmd_default_file_version(void);\n\nstruct tracecmd_output;\nstruct tracecmd_recorder;\nstruct hook_list;\n\n/* --- tracecmd plugins --- */\n\nenum tracecmd_context {\n\tTRACECMD_INPUT,\n\tTRACECMD_OUTPUT,\n};\n\nenum tracecmd_plugin_flag {\n\tTRACECMD_DISABLE_SYS_PLUGINS\t= 1,\n\tTRACECMD_DISABLE_PLUGINS\t= 1 << 1,\n};\n\nstruct trace_plugin_context;\n\nstruct trace_plugin_context *\ntracecmd_plugin_context_create(enum tracecmd_context context, void *data);\n\nvoid tracecmd_plugin_set_flag(struct trace_plugin_context *context,\n\t\t\t      enum tracecmd_plugin_flag flag);\n\n#define TRACECMD_PLUGIN_LOADER tracecmd_plugin_loader\n#define TRACECMD_PLUGIN_UNLOADER tracecmd_plugin_unloader\n#define TRACECMD_PLUGIN_ALIAS tracecmd_plugin_alias\n#define _MAKE_STR(x)\t#x\n#define MAKE_STR(x)\t_MAKE_STR(x)\n#define TRACECMD_PLUGIN_LOADER_NAME MAKE_STR(TRACECMD_PLUGIN_LOADER)\n#define TRACECMD_PLUGIN_UNLOADER_NAME MAKE_STR(TRACECMD_PLUGIN_UNLOADER)\n#define TRACECMD_PLUGIN_ALIAS_NAME MAKE_STR(TRACECMD_PLUGIN_ALIAS)\n\ntypedef int (*tracecmd_plugin_load_func)(struct trace_plugin_context *trace);\ntypedef int (*tracecmd_plugin_unload_func)(struct trace_plugin_context *trace);\n\nstruct tracecmd_input *\ntracecmd_plugin_context_input(struct trace_plugin_context *trace_context);\nstruct tracecmd_output *\ntracecmd_plugin_context_output(struct trace_plugin_context *trace_context);\n\nvoid tracecmd_set_quiet(struct tracecmd_output *handle, bool set_quiet);\nbool tracecmd_get_quiet(struct tracecmd_output *handle);\nvoid tracecmd_set_out_clock(struct tracecmd_output *handle, const char *clock);\nconst char *tracecmd_get_trace_clock(struct tracecmd_input *handle);\n\nconst char *tracecmd_get_cpustats(struct tracecmd_input *handle);\nconst char *tracecmd_get_uname(struct tracecmd_input *handle);\nconst char *tracecmd_get_version(struct tracecmd_input *handle);\noff_t tracecmd_get_cpu_file_size(struct tracecmd_input *handle, int cpu);\n\nstatic inline int tracecmd_host_bigendian(void)\n{\n\tunsigned char str[] = { 0x1, 0x2, 0x3, 0x4 };\n\tunsigned int *ptr;\n\n\tptr = (unsigned int *)str;\n\treturn *ptr == 0x01020304;\n}\n\n/* --- Opening and Reading the trace.dat file --- */\n\nenum tracecmd_file_states {\n\tTRACECMD_FILE_ALLOCATED = 0,\n\tTRACECMD_FILE_INIT,\n\tTRACECMD_FILE_HEADERS,\n\tTRACECMD_FILE_FTRACE_EVENTS,\n\tTRACECMD_FILE_ALL_EVENTS,\n\tTRACECMD_FILE_KALLSYMS,\n\tTRACECMD_FILE_PRINTK,\n\tTRACECMD_FILE_CMD_LINES,\n\tTRACECMD_FILE_CPU_COUNT,\n\tTRACECMD_FILE_OPTIONS,\n\tTRACECMD_FILE_CPU_LATENCY,\n\tTRACECMD_FILE_CPU_FLYRECORD,\n};\n\nenum {\n\tTRACECMD_OPTION_DONE,\n\tTRACECMD_OPTION_DATE,\n\tTRACECMD_OPTION_CPUSTAT,\n\tTRACECMD_OPTION_BUFFER,\n\tTRACECMD_OPTION_TRACECLOCK,\n\tTRACECMD_OPTION_UNAME,\n\tTRACECMD_OPTION_HOOK,\n\tTRACECMD_OPTION_OFFSET,\n\tTRACECMD_OPTION_CPUCOUNT,\n\tTRACECMD_OPTION_VERSION,\n\tTRACECMD_OPTION_PROCMAPS,\n\tTRACECMD_OPTION_TRACEID,\n\tTRACECMD_OPTION_TIME_SHIFT,\n\tTRACECMD_OPTION_GUEST,\n\tTRACECMD_OPTION_TSC2NSEC,\n\tTRACECMD_OPTION_STRINGS,\n\tTRACECMD_OPTION_HEADER_INFO,\n\tTRACECMD_OPTION_FTRACE_EVENTS,\n\tTRACECMD_OPTION_EVENT_FORMATS,\n\tTRACECMD_OPTION_KALLSYMS,\n\tTRACECMD_OPTION_PRINTK,\n\tTRACECMD_OPTION_CMDLINES,\n\tTRACECMD_OPTION_BUFFER_TEXT,\n\tTRACECMD_OPTION_BTF_FILE,\n\tTRACECMD_OPTION_LAST_BOOT_INFO,\n\tTRACECMD_OPTION_MODULES_FILE,\n\tTRACECMD_OPTION_MAX,\n};\n\nenum {\n\tTRACECMD_FL_IGNORE_DATE\t\t= (1 << 0),\n\tTRACECMD_FL_BUFFER_INSTANCE\t= (1 << 1),\n\tTRACECMD_FL_IN_USECS\t\t= (1 << 2),\n\tTRACECMD_FL_RAW_TS\t\t= (1 << 3),\n\tTRACECMD_FL_SECTIONED\t\t= (1 << 4),\n\tTRACECMD_FL_COMPRESSION\t\t= (1 << 5),\n};\n\nstruct tracecmd_ftrace {\n\tstruct tracecmd_input\t\t*handle;\n\tstruct tep_event *fgraph_ret_event;\n\tint fgraph_ret_id;\n\tint long_size;\n};\n\nstruct tracecmd_proc_addr_map {\n\tsize_t\t\t\tstart;\n\tsize_t\t\t\tend;\n\tchar\t\t\t*lib_name;\n};\n\ntypedef void (*tracecmd_show_data_func)(struct tracecmd_input *handle,\n\t\t\t\t\tstruct tep_record *record);\ntypedef void (*tracecmd_handle_init_func)(struct tracecmd_input *handle,\n\t\t\t\t\t  struct hook_list *hook, int global);\n\nstruct tracecmd_input *tracecmd_alloc(const char *file, int flags);\nstruct tracecmd_input *tracecmd_alloc_fd(int fd, int flags);\nvoid tracecmd_ref(struct tracecmd_input *handle);\nint tracecmd_read_headers(struct tracecmd_input *handle,\n\t\t\t  enum tracecmd_file_states state);\nint tracecmd_get_parsing_failures(struct tracecmd_input *handle);\nint tracecmd_page_size(struct tracecmd_input *handle);\nint tracecmd_copy_headers(struct tracecmd_input *in_handle,\n\t\t\t  struct tracecmd_output *out_handle,\n\t\t\t  enum tracecmd_file_states start_state,\n\t\t\t  enum tracecmd_file_states end_state);\nint tracecmd_copy_buffer_descr(struct tracecmd_input *in_handle,\n\t\t\t       struct tracecmd_output *out_handle);\nint tracecmd_copy_options(struct tracecmd_input *in_handle,\n\t\t\t  struct tracecmd_output *out_handle);\nint tcmd_copy_trace_data(struct tracecmd_input *in_handle,\n\t\t\t struct tracecmd_output *out_handle);\nvoid tracecmd_set_flag(struct tracecmd_input *handle, int flag);\nvoid tracecmd_clear_flag(struct tracecmd_input *handle, int flag);\nunsigned long tracecmd_get_flags(struct tracecmd_input *handle);\nenum tracecmd_file_states tracecmd_get_file_state(struct tracecmd_input *handle);\nint tracecmd_enable_tsync(struct tracecmd_input *handle, bool enable);\n\nvoid tracecmd_parse_trace_clock(struct tracecmd_input *handle, char *file, int size);\n\nint tracecmd_make_pipe(struct tracecmd_input *handle, int cpu, int fd, int cpus);\n\nint tracecmd_is_buffer_instance(struct tracecmd_input *handle);\n\nvoid tracecmd_set_ts_offset(struct tracecmd_input *handle, long long offset);\nvoid tracecmd_set_ts2secs(struct tracecmd_input *handle, unsigned long long hz);\n\nvoid tracecmd_print_events(struct tracecmd_input *handle, const char *regex);\n\nstruct hook_list *tracecmd_hooks(struct tracecmd_input *handle);\n\nvoid tracecmd_print_stats(struct tracecmd_input *handle);\nvoid tracecmd_print_uname(struct tracecmd_input *handle);\nvoid tracecmd_print_version(struct tracecmd_input *handle);\n\nint tracecmd_latency_data_read(struct tracecmd_input *handle, char **buf, size_t *size);\n\nstruct tep_record *\ntracecmd_read_prev(struct tracecmd_input *handle, struct tep_record *record);\n\nstruct tep_record *\ntracecmd_peek_next_data(struct tracecmd_input *handle, int *rec_cpu);\n\nstruct tep_record *\ntracecmd_translate_data(struct tracecmd_input *handle,\n\t\t\tvoid *ptr, int size);\nstruct tep_record *\ntracecmd_read_cpu_last(struct tracecmd_input *handle, int cpu);\nint tracecmd_refresh_record(struct tracecmd_input *handle,\n\t\t\t    struct tep_record *record);\n\nint tracecmd_set_cpu_to_timestamp(struct tracecmd_input *handle,\n\t\t\t\t  int cpu, unsigned long long ts);\nvoid\ntracecmd_set_all_cpus_to_timestamp(struct tracecmd_input *handle,\n\t\t\t\t   unsigned long long time);\n\nint tracecmd_set_cursor(struct tracecmd_input *handle,\n\t\t\tint cpu, size_t offset);\nunsigned long long\ntracecmd_get_cursor(struct tracecmd_input *handle, int cpu);\n\nunsigned long tracecmd_get_in_file_version(struct tracecmd_input *handle);\nsize_t tracecmd_get_options_offset(struct tracecmd_input *handle);\nint tracecmd_get_file_compress_proto(struct tracecmd_input *handle,\n\t\t\t\t     const char **name, const char **version);\n\nint tracecmd_ftrace_overrides(struct tracecmd_input *handle, struct tracecmd_ftrace *finfo);\nbool tracecmd_get_use_trace_clock(struct tracecmd_input *handle);\ntracecmd_show_data_func\ntracecmd_get_show_data_func(struct tracecmd_input *handle);\nvoid tracecmd_set_show_data_func(struct tracecmd_input *handle,\n\t\t\t\t tracecmd_show_data_func func);\n\nint tracecmd_record_at_buffer_start(struct tracecmd_input *handle, struct tep_record *record);\nunsigned long long tracecmd_page_ts(struct tracecmd_input *handle,\n\t\t\t\t    struct tep_record *record);\nunsigned int tracecmd_record_ts_delta(struct tracecmd_input *handle,\n\t\t\t\t      struct tep_record *record);\n\nstruct tracecmd_proc_addr_map *\ntracecmd_search_task_map(struct tracecmd_input *handle,\n\t\t\t int pid, unsigned long long addr);\n#ifndef SWIG\n/* hack for function graph work around */\nextern __thread struct tracecmd_input *tracecmd_curr_thread_handle;\n#endif\n\n\n/* --- Creating and Writing the trace.dat file --- */\n\nstruct tracecmd_event_list {\n\tstruct tracecmd_event_list\t*next;\n\tconst char\t\t\t*glob;\n};\n\nstruct tracecmd_option;\nstruct tracecmd_msg_handle;\n\nint tracecmd_output_set_msg(struct tracecmd_output *handle,\n\t\t\t    struct tracecmd_msg_handle *msg_handle);\nint tracecmd_output_set_trace_dir(struct tracecmd_output *handle, const char *tracing_dir);\nint tracecmd_output_set_kallsyms(struct tracecmd_output *handle, const char *kallsyms);\nint tracecmd_output_set_from_input(struct tracecmd_output *handle, struct tracecmd_input *ihandle);\nint tracecmd_output_set_version(struct tracecmd_output *handle, int file_version);\nint tracecmd_output_set_compression(struct tracecmd_output *handle, const char *compression);\nint tracecmd_output_write_headers(struct tracecmd_output *handle,\n\t\t\t\t  struct tracecmd_event_list *list);\n\nstruct tracecmd_output *tracecmd_output_create(const char *output_file);\nstruct tracecmd_output *tracecmd_output_create_fd(int fd);\nstruct tracecmd_output *tracecmd_create_file_latency(const char *output_file, int cpus,\n\t\t\t\t\t\t     int file_version, const char *compression);\n\nstruct tracecmd_option *tracecmd_add_option(struct tracecmd_output *handle,\n\t\t\t\t\t    unsigned short id, int size,\n\t\t\t\t\t    const void *data);\nstruct tracecmd_option *\ntracecmd_add_option_v(struct tracecmd_output *handle,\n\t\t      unsigned short id, const struct iovec *vector, int count);\n\nint tracecmd_add_buffer_info(struct tracecmd_output *handle, const char *name, int cpus);\nint tracecmd_write_buffer_info(struct tracecmd_output *handle);\n\nint tracecmd_write_cpus(struct tracecmd_output *handle, int cpus);\nint tracecmd_write_cmdlines(struct tracecmd_output *handle);\nint tracecmd_prepare_options(struct tracecmd_output *handle, off_t offset, int whence);\nint tracecmd_write_options(struct tracecmd_output *handle);\nint tracecmd_write_meta_strings(struct tracecmd_output *handle);\nint tracecmd_append_options(struct tracecmd_output *handle);\nvoid tracecmd_output_close(struct tracecmd_output *handle);\nvoid tracecmd_output_flush(struct tracecmd_output *handle);\nvoid tracecmd_output_free(struct tracecmd_output *handle);\nstruct tracecmd_output *tracecmd_copy(struct tracecmd_input *ihandle, const char *file,\n\t\t\t\t      enum tracecmd_file_states state, int file_version,\n\t\t\t\t      const char *compression);\n\nint tracecmd_write_cpu_data(struct tracecmd_output *handle,\n\t\t\t    int cpus, char * const *cpu_data_files, const char *buff_name);\nint tracecmd_append_cpu_data(struct tracecmd_output *handle,\n\t\t\t     int cpus, char * const *cpu_data_files);\nint tracecmd_append_buffer_cpu_data(struct tracecmd_output *handle,\n\t\t\t\t    const char *name, int cpus, char * const *cpu_data_files);\nstruct tracecmd_output *tracecmd_get_output_handle_fd(int fd);\nunsigned long tracecmd_get_out_file_version(struct tracecmd_output *handle);\nsize_t tracecmd_get_out_file_offset(struct tracecmd_output *handle);\nint tracecmd_append_btf_file(struct tracecmd_output *handle);\nint tracecmd_append_modules_file(struct tracecmd_output *handle);\n\n/* --- Reading the Fly Recorder Trace --- */\n\nenum {\n\tTRACECMD_RECORD_NOSPLICE\t= (1 << 0),\t/* Use read instead of splice */\n\tTRACECMD_RECORD_SNAPSHOT\t= (1 << 1),\t/* Extract from snapshot */\n\tTRACECMD_RECORD_BLOCK_SPLICE\t= (1 << 2),\t/* Block on splice write */\n\tTRACECMD_RECORD_NOBRASS\t\t= (1 << 3),\t/* Splice directly without a brass pipe */\n\tTRACECMD_RECORD_POLL\t\t= (1 << 4),\t/* Use O_NONBLOCK, poll trace buffers */\n};\n\nvoid tracecmd_free_recorder(struct tracecmd_recorder *recorder);\nstruct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu, unsigned flags);\nstruct tracecmd_recorder *tracecmd_create_recorder_fd(int fd, int cpu, unsigned flags);\nstruct tracecmd_recorder *tracecmd_create_recorder_virt(const char *file, int cpu, unsigned flags, int trace_fd, int maxkb);\nstruct tracecmd_recorder *tracecmd_create_recorder_maxkb(const char *file, int cpu, unsigned flags, int maxkb);\nstruct tracecmd_recorder *tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, struct tracefs_instance *instance);\nstruct tracecmd_recorder *tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags, struct tracefs_instance *instance);\nstruct tracecmd_recorder *tracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags, struct tracefs_instance *instance, int maxkb);\n\nint tracecmd_start_recording(struct tracecmd_recorder *recorder, unsigned long sleep);\nint tracecmd_stop_recording(struct tracecmd_recorder *recorder);\nlong tracecmd_flush_recording(struct tracecmd_recorder *recorder, bool finish);\n\nenum tracecmd_msg_flags {\n\tTRACECMD_MSG_FL_USE_TCP\t\t= 1 << 0,\n\tTRACECMD_MSG_FL_USE_VSOCK\t= 1 << 1,\n\tTRACECMD_MSG_FL_PROXY\t\t= 1 << 2,\n};\n\n#define MSG_CACHE_FILE \"/tmp/trace_msg_cacheXXXXXX\"\n\n/* for both client and server */\nstruct tracecmd_msg_handle {\n\tint\t\t\tfd;\n\tshort\t\t\tcpu_count;\n\tshort\t\t\tversion;\t/* Current protocol version */\n\tunsigned long\t\tflags;\n\toff_t\t\t\tcache_start_offset;\n\tbool\t\t\tdone;\n\tbool\t\t\tcache;\n\tint\t\t\tcfd;\n#ifndef HAVE_MEMFD_CREATE\n\tchar\t\t\tcfile[sizeof(MSG_CACHE_FILE)];\n#endif\n};\n\nstruct tracecmd_tsync_protos {\n\tchar **names;\n};\n\nstruct tracecmd_msg_handle *\ntracecmd_msg_handle_alloc(int fd, unsigned long flags);\nint tracecmd_msg_handle_cache(struct tracecmd_msg_handle *msg_handle);\n\n/* Closes the socket and frees the handle */\nvoid tracecmd_msg_handle_close(struct tracecmd_msg_handle *msg_handle);\n\n/* for clients */\nint tracecmd_msg_send_init_data(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tunsigned int **client_ports);\nint tracecmd_msg_data_send(struct tracecmd_msg_handle *msg_handle,\n\t\t\t       const char *buf, int size);\nint tracecmd_msg_finish_sending_data(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_flush_data(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_send_close_msg(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_send_close_resp_msg(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_wait_close(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_wait_close_resp(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_cont(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_wait(struct tracecmd_msg_handle *msg_handle);\n\n/* for server */\nint tracecmd_msg_initial_setting(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_send_port_array(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t unsigned *ports);\nint tracecmd_msg_read_data(struct tracecmd_msg_handle *msg_handle, int ofd);\nint tracecmd_msg_collect_data(struct tracecmd_msg_handle *msg_handle, int ofd);\nbool tracecmd_msg_done(struct tracecmd_msg_handle *msg_handle);\nvoid tracecmd_msg_set_done(struct tracecmd_msg_handle *msg_handle);\nint tracecmd_msg_read_options(struct tracecmd_msg_handle *msg_handle,\n\t\t\t      struct tracecmd_output *handle);\nint tracecmd_msg_send_options(struct tracecmd_msg_handle *msg_handle,\n\t\t\t      struct tracecmd_output *handle);\n\nint tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tint argc, char **argv, bool use_fifos,\n\t\t\t\tunsigned long long trace_id,\n\t\t\t\tstruct tracecmd_tsync_protos *protos);\nint tracecmd_msg_send_trace_proxy(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t  int argc, char **argv, bool use_fifos,\n\t\t\t\t  unsigned long long trace_id,\n\t\t\t\t  struct tracecmd_tsync_protos *protos,\n\t\t\t\t  unsigned int nr_cpus,\n\t\t\t\t  unsigned int siblings);\nint tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tint *argc, char ***argv, bool *use_fifos,\n\t\t\t\tunsigned long long *trace_id,\n\t\t\t\tstruct tracecmd_tsync_protos **protos);\nint tracecmd_msg_recv_trace_proxy(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t  int *argc, char ***argv, bool *use_fifos,\n\t\t\t\t  unsigned long long *trace_id,\n\t\t\t\t  struct tracecmd_tsync_protos **protos,\n\t\t\t\t  unsigned int *cpus, unsigned int *siblings);\n\nint tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t int nr_cpus, int page_size,\n\t\t\t\t unsigned int *ports, bool use_fifos,\n\t\t\t\t unsigned long long trace_id,\n\t\t\t\t const char *tsync_proto, unsigned int tsync_port);\nint tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t int *nr_cpus, int *page_size,\n\t\t\t\t unsigned int **ports, bool *use_fifos,\n\t\t\t\t unsigned long long *trace_id,\n\t\t\t\t char **tsync_proto,\n\t\t\t\t unsigned int *tsync_port);\n\nint tracecmd_msg_send_time_sync(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tchar *sync_protocol, unsigned int sync_msg_id,\n\t\t\t\tunsigned int payload_size, char *payload);\nint tracecmd_msg_recv_time_sync(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tchar *sync_protocol,\n\t\t\t\tunsigned int *sync_msg_id,\n\t\t\t\tunsigned int *payload_size, char **payload);\n\nenum tracecmd_clocks {\n\tTRACECMD_CLOCK_UNKNOWN\t= 0,\n\tTRACECMD_CLOCK_LOCAL\t= 1,\n\tTRACECMD_CLOCK_GLOBAL\t= 1 << 1,\n\tTRACECMD_CLOCK_COUNTER\t= 1 << 2,\n\tTRACECMD_CLOCK_UPTIME\t= 1 << 3,\n\tTRACECMD_CLOCK_PERF\t= 1 << 4,\n\tTRACECMD_CLOCK_MONO\t= 1 << 5,\n\tTRACECMD_CLOCK_MONO_RAW\t= 1 << 6,\n\tTRACECMD_CLOCK_BOOT\t= 1 << 7,\n\tTRACECMD_CLOCK_X86_TSC\t= 1 << 8\n};\n\nenum tracecmd_clocks tracecmd_clock_str2id(const char *clock);\nconst char *tracecmd_clock_id2str(enum tracecmd_clocks clock);\n\n/* --- Timestamp synchronization --- */\n\nstruct tracecmd_time_sync;\n#define TRACECMD_TSYNC_PNAME_LENGTH\t16\n#define TRACECMD_TSYNC_PROTO_NONE\t\"none\"\n\nenum{\n\tTRACECMD_TIME_SYNC_CMD_PROBE\t= 1,\n\tTRACECMD_TIME_SYNC_CMD_STOP\t= 2,\n};\n\nenum tracecmd_time_sync_role {\n\tTRACECMD_TIME_SYNC_ROLE_HOST\t= (1 << 0),\n\tTRACECMD_TIME_SYNC_ROLE_GUEST\t= (1 << 1),\n\tTRACECMD_TIME_SYNC_ROLE_CLIENT\t= (1 << 2),\n\tTRACECMD_TIME_SYNC_ROLE_SERVER\t= (1 << 3),\n};\n\n/* Timestamp synchronization flags */\n#define TRACECMD_TSYNC_FLAG_INTERPOLATE\t0x1\n\nvoid tracecmd_tsync_init(void);\nint tracecmd_tsync_proto_getall(struct tracecmd_tsync_protos **protos, const char *clock, int role);\nbool tcmd_tsync_proto_is_supported(const char *proto_name);\nstruct tracecmd_time_sync *\ntracecmd_tsync_with_host(int fd, const char *proto, const char *clock,\n\t\t\t int remote_id, int local_id);\nint tracecmd_tsync_with_host_stop(struct tracecmd_time_sync *tsync);\nstruct tracecmd_time_sync *\ntracecmd_tsync_with_guest(unsigned long long trace_id, int loop_interval,\n\t\t\t  unsigned int fd, int guest_pid,\n\t\t\t  int guest_cpus, const char *proto_name, const char *clock);\nint tracecmd_tsync_with_guest_stop(struct tracecmd_time_sync *tsync);\nint tracecmd_tsync_get_offsets(struct tracecmd_time_sync *tsync, int cpu,\n\t\t\t       int *count, long long **ts,\n\t\t\t       long long **offsets, long long **scalings, long long **frac);\nconst char *tracecmd_tsync_get_proto(const struct tracecmd_tsync_protos *protos,\n\t\t\t\t     const char *clock, enum tracecmd_time_sync_role role);\nvoid tracecmd_tsync_free(struct tracecmd_time_sync *tsync);\nint tracecmd_write_guest_time_shift(struct tracecmd_output *handle,\n\t\t\t\t    struct tracecmd_time_sync *tsync);\n\n/* --- Compression --- */\nstruct tracecmd_compress_chunk {\n\tunsigned int\t\tsize;\n\tunsigned int\t\tzsize;\n\toff_t\t\t\tzoffset;\n\toff_t\t\t\toffset;\n};\nstruct tracecmd_compression;\nstruct tracecmd_compression_proto {\n\tint weight;\n\tconst char *name;\n\tconst char *version;\n\tint (*compress)(void *ctx, const void *in, int in_bytes, void *out, int out_bytes);\n\tint (*uncompress)(void *ctx, const void *in, int in_bytes, void *out, int out_bytes);\n\tunsigned int (*compress_size)(void *ctx, unsigned int bytes);\n\tbool (*is_supported)(const char *name, const char *version);\n\tvoid *(*new_context)(void);\n\tvoid (*free_context)(void *ctx);\n};\n\nstruct tracecmd_compression *tracecmd_compress_alloc(const char *name, const char *version,\n\t\t\t\t\t\t     int fd, struct tep_handle *tep,\n\t\t\t\t\t\t     struct tracecmd_msg_handle *msg_handle);\nvoid tracecmd_compress_destroy(struct tracecmd_compression *handle);\nint tracecmd_compress_block(struct tracecmd_compression *handle);\nint tracecmd_uncompress_block(struct tracecmd_compression *handle);\nvoid tracecmd_compress_reset(struct tracecmd_compression *handle);\nssize_t tracecmd_compress_buffer_read(struct tracecmd_compression *handle, char *dst, size_t len);\nssize_t tracecmd_compress_pread(struct tracecmd_compression *handle, char *dst, size_t len, off_t offset);\nint tracecmd_compress_buffer_write(struct tracecmd_compression *handle,\n\t\t\t\t   const void *data, size_t size);\noff_t tracecmd_compress_lseek(struct tracecmd_compression *handle, off_t offset, int whence);\nint tracecmd_compress_proto_get_name(struct tracecmd_compression *compress,\n\t\t\t\t     const char **name, const char **version);\nbool tracecmd_compress_is_supported(const char *name, const char *version);\nint tracecmd_compress_protos_get(char ***names, char ***versions);\nint tracecmd_compress_proto_register(struct tracecmd_compression_proto *proto);\nint tracecmd_compress_copy_from(struct tracecmd_compression *handle, int fd, int chunk_size,\n\t\t\t\tsize_t *read_size, size_t *write_size);\nint tracecmd_uncompress_copy_to(struct tracecmd_compression *handle, int fd,\n\t\t\t\tsize_t *read_size, size_t *write_size);\nint tracecmd_uncompress_chunk(struct tracecmd_compression *handle,\n\t\t\t      struct tracecmd_compress_chunk *chunk, char *data);\nint tracecmd_load_chunks_info(struct tracecmd_compression *handle,\n\t\t\t      struct tracecmd_compress_chunk **chunks_info);\nvoid *tracecmd_uncompress_buffer(struct tracecmd_compression *handle, size_t *size);\n/* --- Plugin handling --- */\nextern struct tep_plugin_option trace_ftrace_options[];\n\nchar **tcmd_util_find_plugin_files(const char *suffix);\nvoid tcmd_util_free_plugin_files(char **files);\n\n/* Used for trace-cmd list */\nvoid tracecmd_ftrace_load_options(void);\n\n/* event hooks */\n\nstruct hook_list {\n\tstruct hook_list\t*next;\n\tstruct buffer_instance\t*instance;\n\tconst char\t\t*hook;\n\tchar\t\t\t*str;\n\tchar\t\t\t*start_system;\n\tchar\t\t\t*start_event;\n\tchar\t\t\t*start_match;\n\tchar\t\t\t*end_system;\n\tchar\t\t\t*end_event;\n\tchar\t\t\t*end_match;\n\tchar\t\t\t*pid;\n\tint\t\t\tmigrate;\n\tint\t\t\tglobal;\n\tint\t\t\tstack;\n};\n\nstruct hook_list *tracecmd_create_event_hook(const char *arg);\nvoid tracecmd_free_hooks(struct hook_list *hooks);\n\nvoid tracecmd_plog(const char *fmt, ...);\nvoid tracecmd_plog_error(const char *fmt, ...);\nint tracecmd_set_logfile(char *logfile);\n\n/* --- System --- */\nunsigned long long tracecmd_generate_traceid(void);\nint tracecmd_count_cpus(void);\n\n/* --- Hack! --- */\nint tracecmd_blk_hack(struct tracecmd_input *handle);\n\n/* --- Stack tracer functions --- */\nint tracecmd_stack_tracer_status(int *status);\n\n/* --- Debugging --- */\nstruct kbuffer *tracecmd_record_kbuf(struct tracecmd_input *handle,\n\t\t\t\t     struct tep_record *record);\nvoid *tracecmd_record_page(struct tracecmd_input *handle,\n\t\t\t   struct tep_record *record);\nvoid *tracecmd_record_offset(struct tracecmd_input *handle,\n\t\t\t     struct tep_record *record);\n#ifdef PERF\n\n#include <linux/perf_event.h>\n\n/* trace-cmd Perf */\nstruct trace_perf {\n\tint fd;\n\tint cpu;\n\tint pid;\n\tint pages;\n\tstruct perf_event_attr pe;\n\tstruct perf_event_mmap_page *mmap;\n};\nint tcmd_perf_init(struct trace_perf *perf, int pages, int cpu, int pid);\nint tcmd_perf_open(struct trace_perf *perf);\nvoid tcmd_perf_close(struct trace_perf *perf);\n#endif\n\n#endif /* _TRACE_CMD_PRIVATE_H */\n"
  },
  {
    "path": "lib/trace-cmd/include/private/trace-filter-hash.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n * Copyright (C) 2018 VMware Inc, Steven Rostedt <rostedt@goodmis.org>\n *\n */\n#ifndef _TRACE_FILTER_HASH_H\n#define _TRACE_FILTER_HASH_H\n\n#include <stdint.h>\n\nstruct tracecmd_filter_id_item {\n\tstruct tracecmd_filter_id_item\t*next;\n\tint\t\t\t\tid;\n};\n\nstruct tracecmd_filter_id {\n\tstruct tracecmd_filter_id_item **hash;\n\tint\t\t\t\tcount;\n};\n\n/**\n * tracecmd_quick_hash - A quick (non secured) hash alogirthm\n * @val: The value to perform the hash on\n * @bits: The size in bits you need to return\n *\n * This is a quick hashing function adapted from Donald E. Knuth's 32\n * bit multiplicative hash.  See The Art of Computer Programming (TAOCP).\n * Multiplication by the Prime number, closest to the golden ratio of\n * 2^32.\n *\n * @bits is used to max the result for use cases that require\n * a power of 2 return value that is less than 32 bits. Any value\n * of @bits greater than 31 (or zero), will simply return the full hash on @val.\n */\nstatic inline uint32_t tracecmd_quick_hash(uint32_t val, unsigned int bits)\n{\n\tval *= UINT32_C(2654435761);\n\n\tif (!bits || bits > 31)\n\t\treturn val;\n\n\treturn val & ((1 << bits) - 1);\n}\n\nstruct tracecmd_filter_id_item *\n  tracecmd_filter_id_find(struct tracecmd_filter_id *hash, int id);\nvoid tracecmd_filter_id_add(struct tracecmd_filter_id *hash, int id);\nvoid tracecmd_filter_id_remove(struct tracecmd_filter_id *hash, int id);\nvoid tracecmd_filter_id_clear(struct tracecmd_filter_id *hash);\nstruct tracecmd_filter_id *tracecmd_filter_id_hash_alloc(void);\nvoid tracecmd_filter_id_hash_free(struct tracecmd_filter_id *hash);\nstruct tracecmd_filter_id *\n  tracecmd_filter_id_hash_copy(struct tracecmd_filter_id *hash);\nint *tracecmd_filter_ids(struct tracecmd_filter_id *hash);\nint tracecmd_filter_id_compare(struct tracecmd_filter_id *hash1,\n\t\t\t       struct tracecmd_filter_id *hash2);\n\nstatic inline int tracecmd_filter_task_count(struct tracecmd_filter_id *hash)\n{\n\treturn hash->count;\n}\n\n#endif /* _TRACE_FILTER_HASH_H */\n"
  },
  {
    "path": "lib/trace-cmd/include/private/trace-hash.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 */\n/*\n * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#ifndef _TRACE_HASH_H\n#define _TRACE_HASH_H\n\nstruct trace_hash_item {\n\tstruct trace_hash_item\t*next;\n\tstruct trace_hash_item\t*prev;\n\tunsigned long long\tkey;\n};\n\nstruct trace_hash {\n\tstruct trace_hash_item\t**buckets;\n\tint\t\t\tnr_buckets;\n\tint\t\t\tpower;\n};\n\nint tcmd_hash_init(struct trace_hash *hash, int buckets);\nvoid tcmd_hash_free(struct trace_hash *hash);\nint tcmd_hash_add(struct trace_hash *hash, struct trace_hash_item *item);\nint tcmd_hash_empty(struct trace_hash *hash);\n\nstatic inline void trace_hash_del(struct trace_hash_item *item)\n{\n\tstruct trace_hash_item *prev = item->prev;\n\n\tprev->next = item->next;\n\tif (item->next)\n\t\titem->next->prev = prev;\n}\n\n#define trace_hash_for_each_bucket(bucket, hash)\t\t\t\\\n\tfor (bucket = (hash)->buckets;\t\t\t\t\t\\\n\t     (bucket) < (hash)->buckets + (hash)->nr_buckets; (bucket)++)\n\n#define trace_hash_for_each_item(item, bucket)\t\t\t\t\\\n\tfor ((item = *(bucket)); item; item = (item)->next)\n\n#define trace_hash_for_each_item_safe(item, n, bucket)\t\t\\\n\tfor ((item = *(bucket)), n = item ? item->next : NULL; item; \\\n\t     item = n, n = item ? (item)->next : NULL)\n\n#define trace_hash_while_item(item, bucket)\t\\\n\twhile ((item = *(bucket)))\n\ntypedef int (*trace_hash_func)(struct trace_hash_item *item, void *data);\n\nstruct trace_hash_item *\ntcmd_hash_find(struct trace_hash *hash, unsigned long long key,\n\t\ttrace_hash_func match, void *data);\n\n#endif /* _TRACE_HASH_H */\n"
  },
  {
    "path": "lib/trace-cmd/include/private/trace-msg.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n#ifndef _TRACE_MSG_H_\n#define _TRACE_MSG_H_\n\n#include <stdbool.h>\n\n#define UDP_MAX_PACKET\t(65536 - 20)\n#define V3_MAGIC\t\"766679\\0\"\n#define V3_CPU\t\t\"-1V3\"\n\n#define V1_PROTOCOL\t1\n#define V3_PROTOCOL\t3\n\nextern unsigned int page_size;\n\n#endif /* _TRACE_MSG_H_ */\n"
  },
  {
    "path": "lib/trace-cmd/include/private/trace-rbtree.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2023 Google, Steven Rostedt <rostedt@goodmis.org>\n *\n */\n#ifndef _TRACE_RBTREE_H\n#define _TRACE_RBTREE_H\n\nstruct trace_rbtree_node {\n\tstruct trace_rbtree_node\t*parent;\n\tstruct trace_rbtree_node\t*left;\n\tstruct trace_rbtree_node\t*right;\n\tint\t\t\t\tcolor;\n};\n\ntypedef int (*trace_rbtree_cmp_fn)(const struct trace_rbtree_node *A, const struct trace_rbtree_node *B);\n\ntypedef int (*trace_rbtree_search_fn)(const struct trace_rbtree_node *n, const void *data);\n\nstruct trace_rbtree {\n\tstruct trace_rbtree_node\t*node;\n\ttrace_rbtree_search_fn\t\tsearch;\n\ttrace_rbtree_cmp_fn\t\tcmp;\n\tsize_t\t\t\t\tnr_nodes;\n};\n\nvoid tcmd_rbtree_init(struct trace_rbtree *tree, trace_rbtree_cmp_fn cmp_fn,\n\t\t      trace_rbtree_search_fn search_fn);\nstruct trace_rbtree_node *trace_rbtree_find(struct trace_rbtree *tree, const void *data);\nvoid trace_rbtree_delete(struct trace_rbtree *tree, struct trace_rbtree_node *node);\nint tcmd_rbtree_insert(struct trace_rbtree *tree, struct trace_rbtree_node *node);\nstruct trace_rbtree_node *trace_rbtree_pop_nobalance(struct trace_rbtree *tree);\n\n#endif /* _TRACE_RBTREE_H */\n"
  },
  {
    "path": "lib/trace-cmd/include/trace-cmd-local.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#ifndef _TRACE_CMD_LOCAL_H\n#define _TRACE_CMD_LOCAL_H\n\n#include <byteswap.h>\n#include \"trace-cmd-private.h\"\n\n#define FILE_VERSION_DEFAULT\t\t7\n\n/* Can be overridden */\nvoid tracecmd_warning(const char *fmt, ...);\nvoid tracecmd_critical(const char *fmt, ...);\nvoid tracecmd_info(const char *fmt, ...);\n\n#ifndef htonll\n# if __BYTE_ORDER == __LITTLE_ENDIAN\n#define htonll(x) __bswap_64(x)\n#define ntohll(x) __bswap_64(x)\n#else\n#define htonll(x) (x)\n#define ntohll(x) (x)\n#endif\n#endif\n\n#ifdef HAVE_ZLIB\nint tracecmd_zlib_init(void);\n#endif\n\n#ifdef HAVE_ZSTD\nint tracecmd_zstd_init(void);\n#else\nstatic inline int tracecmd_zstd_init(void)\n{\n\treturn 0;\n}\n#endif\n\nstruct data_file_write {\n\tunsigned long long\tfile_size;\n\tunsigned long long\twrite_size;\n\t/* offset in the trace file, where write_size is stored */\n\tunsigned long long\tfile_write_size;\n\tunsigned long long\tdata_offset;\n\t/* offset in the trace file, where data_offset is stored */\n\tunsigned long long\tfile_data_offset;\n};\n\nenum tracecmd_filters tcmd_filter_match(struct tracecmd_filter *filter,\n\t\t\t\t\tstruct tep_record *record);\n\nvoid tcmd_set_guest_map(struct tracecmd_input *handle, struct tracecmd_cpu_map *map);\nstruct tracecmd_cpu_map *tcmd_get_guest_map(struct tracecmd_input *handle);\nvoid tcmd_set_guest_map_cnt(struct tracecmd_input *handle, int count);\nint tcmd_get_guest_map_cnt(struct tracecmd_input *handle);\nvoid tcmd_guest_map_free(struct tracecmd_cpu_map *map);\n\nvoid tracecmd_compress_init(void);\nvoid tracecmd_compress_free(void);\n\nbool tcmd_check_file_state(unsigned long file_version, int current_state, int new_state);\nbool tcmd_check_out_state(struct tracecmd_output *handle, int new_state);\n\nint tcmd_out_uncompress_block(struct tracecmd_output *handle);\nint tcmd_out_compression_start(struct tracecmd_output *handle);\nint tcmd_out_compression_end(struct tracecmd_output *handle);\nvoid tcmd_out_compression_reset(struct tracecmd_output *handle);\n\nvoid tcmd_out_set_file_state(struct tracecmd_output *handle, int new_state);\nint tcmd_out_save_options_offset(struct tracecmd_output *handle,\n\t\t\t\t unsigned long long start);\nunsigned long long tcmd_out_copy_fd_compress(struct tracecmd_output *handle,\n\t\t\t\t\t     int fd, unsigned long long max,\n\t\t\t\t\t     unsigned long long *write_size, int page);\nvoid tcmd_in_uncompress_reset(struct tracecmd_input *handle);\nint tcmd_in_uncompress_block(struct tracecmd_input *handle);\n\nunsigned long long\ntcmd_out_write_section_header(struct tracecmd_output *handle, unsigned short header_id,\n\t\t\t      char *description, int flags, bool option);\nint tcmd_out_update_section_header(struct tracecmd_output *handle, unsigned long long offset);\n\nlong long tcmd_do_write_check(struct tracecmd_output *handle, const void *data, long long size);\n\nstruct tracecmd_option *\ntcmd_out_add_buffer_option(struct tracecmd_output *handle, const char *name,\n\t\t\t   unsigned short id, unsigned long long data_offset,\n\t\t\t   int cpus, struct data_file_write *cpu_data, int page_size);\n\nstruct cpu_data_source {\n\tint fd;\n\tssize_t size;\n\toff_t offset;\n};\n\nint tcmd_out_write_cpu_data(struct tracecmd_output *handle, int cpus,\n\t\t\t    struct cpu_data_source *data, const char *buff_name);\nint tcmd_out_write_emty_cpu_data(struct tracecmd_output *handle, int cpus);\noff_t tcmd_msg_lseek(struct tracecmd_msg_handle *msg_handle, off_t offset, int whence);\nunsigned long long tcmd_get_last_option_offset(struct tracecmd_input *handle);\nunsigned int tcmd_get_meta_strings_size(struct tracecmd_input *handle);\nint tcmd_append_options(struct tracecmd_output *handle, void *buf, size_t len);\nvoid *tcmd_get_options(struct tracecmd_output *handle, size_t *len);\n\n/* filters */\nstruct tracecmd_filter *tcmd_filter_get(struct tracecmd_input *handle);\nvoid tcmd_filter_set(struct tracecmd_input *handle, struct tracecmd_filter *filter);\nvoid tcmd_filter_free(struct tracecmd_filter *filter);\n\n#endif /* _TRACE_CMD_LOCAL_H */\n"
  },
  {
    "path": "lib/trace-cmd/include/trace-hash-local.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 */\n/*\n * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#ifndef _TRACE_HASH_LOCAL_H\n#define _TRACE_HASH_LOCAL_H\n\nstatic inline unsigned int trace_hash(unsigned int val)\n{\n\tunsigned int hash, tmp;\n\n\thash = 12546869;\t/* random prime */\n\n\t/*\n\t * The following hash is based off of Paul Hsieh's super fast hash:\n\t *  http://www.azillionmonkeys.com/qed/hash.html\n\t * Note, he released this code unde the GPL 2.0 license, which\n\t *  is the same as the license for the programs that use it here.\n\t */\n\n\thash +=\t(val & 0xffff);\n\ttmp = (val >> 16) ^ hash;\n\thash = (hash << 16) ^ tmp;\n\thash += hash >> 11;\n\n\thash ^= hash << 3;\n\thash += hash >> 5;\n\thash ^= hash << 4;\n\thash += hash >> 17;\n\thash ^= hash << 25;\n\thash += hash >> 6;\n\n\treturn hash;\n}\n\nstatic inline unsigned int trace_hash_str(char *str)\n{\n\tint val = 0;\n\tint i;\n\n\tfor (i = 0; str[i]; i++)\n\t\tval += ((int)str[i]) << (i & 0xf);\n\treturn trace_hash(val);\n}\n#endif /* _TRACE_HASH_LOCAL_H */\n"
  },
  {
    "path": "lib/trace-cmd/include/trace-tsync-local.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n *\n */\n#ifndef _TRACE_TSYNC_LOCAL_H\n#define _TRACE_TSYNC_LOCAL_H\n\n#include <stdbool.h>\n\nstruct tsync_proto;\n\nstruct tracecmd_time_sync {\n\tpthread_t\t\t\tthread;\n\tbool\t\t\t\tthread_running;\n\tunsigned long long\t\ttrace_id;\n\tchar\t\t\t\t*proto_name;\n\tint\t\t\t\tloop_interval;\n\tpthread_mutex_t\t\t\tlock;\n\tpthread_cond_t\t\t\tcond;\n\tpthread_barrier_t\t\tfirst_sync;\n\tchar\t\t\t\t*clock_str;\n\tstruct tracecmd_msg_handle\t*msg_handle;\n\tstruct tsync_proto\t\t*proto;\n\tvoid\t\t\t\t*context;\n\tint\t\t\t\tguest_pid;\n\tint\t\t\t\tvcpu_count;\n\tint\t\t\t\tremote_id;\n\tint\t\t\t\tlocal_id;\n};\n\nstruct clock_sync_offsets {\n\t/* Arrays with calculated time offsets at given time */\n\tint\t\t\t\tsync_size;\t/* Allocated size of sync_ts,\n\t\t\t\t\t\t\t * sync_offsets, sync_scalings and sync_frac\n\t\t\t\t\t\t\t */\n\tint\t\t\t\tsync_count;\t/* Number of elements in sync_ts,\n\t\t\t\t\t\t\t * sync_offsets, sync_scalings and sync_frac\n\t\t\t\t\t\t\t */\n\tlong long\t\t\t*sync_ts;\n\tlong long\t\t\t*sync_offsets;\n\tlong long\t\t\t*sync_scalings;\n\tlong long\t\t\t*sync_frac;\n};\n\nstruct clock_sync_context {\n\tvoid\t\t\t\t*proto_data;\t/* time sync protocol specific data */\n\tbool\t\t\t\tis_server;\t/* server side time sync role */\n\tbool\t\t\t\tis_guest;\t/* guest or host time sync role */\n\tstruct tracefs_instance\t\t*instance;\t/* ftrace buffer, used for time sync events */\n\n\tint\t\t\t\tcpu_count;\n\tstruct clock_sync_offsets\t*offsets;\t/* Array of size cpu_count\n\t\t\t\t\t\t\t * calculated offsets per CPU\n\t\t\t\t\t\t\t */\n\n\t/* Identifiers of local and remote time sync peers */\n\tunsigned int\t\t\tlocal_id;\n\tunsigned int\t\t\tremote_id;\n};\n\nint tracecmd_tsync_proto_register(const char *proto_name, int accuracy, int roles,\n\t\t\t\t  int supported_clocks, unsigned int flags,\n\t\t\t\t  int (*init)(struct tracecmd_time_sync *),\n\t\t\t\t  int (*free)(struct tracecmd_time_sync *),\n\t\t\t\t  int (*calc)(struct tracecmd_time_sync *,\n\t\t\t\t\t      long long *, long long *, long long*,\n\t\t\t\t\t      long long *, unsigned int));\nint tracecmd_tsync_proto_unregister(char *proto_name);\nint ptp_clock_sync_register(void);\n\n#ifdef VSOCK\nint kvm_clock_sync_register(void);\n#else\nstatic inline int kvm_clock_sync_register(void)\n{\n\treturn 0;\n}\n#endif\n\n#endif /* _TRACE_TSYNC_LOCAL_H */\n"
  },
  {
    "path": "lib/trace-cmd/include/trace-write-local.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#ifndef _TRACE_WRITE_LOCAL_H\n#define _TRACE_WRITE_LOCAL_H\n\n/* Local for trace-input.c, trace-output.c and trace-msg.c */\n\nstatic inline ssize_t __do_write(int fd, const void *data, size_t size)\n{\n\tssize_t tot = 0;\n\tssize_t w;\n\n\tdo {\n\t\tw = write(fd, data + tot, size - tot);\n\t\ttot += w;\n\n\t\tif (!w)\n\t\t\tbreak;\n\t\tif (w < 0)\n\t\t\treturn w;\n\t} while (tot != size);\n\n\treturn tot;\n}\n\nstatic inline ssize_t\n__do_write_check(int fd, const void *data, size_t size)\n{\n\tssize_t ret;\n\n\tret = __do_write(fd, data, size);\n\tif (ret < 0)\n\t\treturn ret;\n\tif (ret != size)\n\t\treturn -1;\n\n\treturn 0;\n}\n\n#endif /* _TRACE_WRITE_LOCAL_H */\n"
  },
  {
    "path": "lib/trace-cmd/meson.build",
    "content": "# SPDX-License-Identifier: LGPL-2.1\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nsources = [\n    'trace-hash.c',\n    'trace-rbtree.c',\n    'trace-hooks.c',\n    'trace-input.c',\n    'trace-output.c',\n    'trace-recorder.c',\n    'trace-util.c',\n    'trace-filter-hash.c',\n    'trace-filter.c',\n    'trace-msg.c',\n    'trace-plugin.c',\n    'trace-maps.c',\n    'trace-timesync.c',\n    'trace-timesync-ptp.c',\n    'trace-compress.c',\n    'trace-blk-hack.c',\n    'trace-ftrace.c',\n]\n\nif perf_defined\n    sources += 'trace-perf.c'\nendif\n\nif vsock_defined\n    sources += 'trace-timesync-kvm.c'\nendif\n\nif zlib_dep.found()\n    sources += 'trace-compress-zlib.c'\nendif\n\nif libzstd_dep.found()\n    sources += 'trace-compress-zstd.c'\nendif\n\nif libtracecmd_standalone_build\n    libtracecmd = library(\n        'tracecmd',\n        sources,\n        version: library_version,\n        dependencies: [\n            libtraceevent_dep,\n            libtracefs_dep,\n            threads_dep,\n            dl_dep,\n            zlib_dep,\n            libzstd_dep,\n            audit_dep],\n        include_directories: [\n            libtracecmd_incdir,\n            libtracecmd_private_incdir,\n            libtracecmd_ext_incdir],\n        install: true)\n\n    pkg = import('pkgconfig')\n    pkg.generate(\n        libtracecmd,\n        subdirs: 'trace-cmd',\n        libraries: [\n            libtracefs_dep,\n            libtraceevent_dep],\n        filebase: meson.project_name(),\n        name: meson.project_name(),\n        version: meson.project_version(),\n        description: 'Library for creating and reading trace-cmd data files',\n        url: 'https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/')\n\n    libtracecmd_dep = declare_dependency(\n       include_directories: ['.'],\n       link_with: libtracecmd)\nelse\n    static_libtracecmd = static_library(\n        'tracecmd',\n        sources,\n        dependencies: [\n            libtraceevent_dep,\n            libtracefs_dep,\n            threads_dep,\n            dl_dep,\n            zlib_dep,\n            libzstd_dep,\n            audit_dep],\n        include_directories: [\n            libtracecmd_incdir,\n            libtracecmd_private_incdir,\n            libtracecmd_ext_incdir],\n        install: false)\nendif\n"
  },
  {
    "path": "lib/trace-cmd/plugins/Makefile",
    "content": "include $(src)/scripts/utils.mk\n\nbdir:=$(obj)/lib/trace-cmd/plugins\n\nPLUGIN_OBJS =\n\nPLUGIN_OBJS := $(PLUGIN_OBJS:%.o=$(bdir)/%.o)\nPLUGIN_BUILD := $(PLUGIN_OBJS:$(bdir)/%.o=$(bdir)/%.so)\n\nPLUGINS := $(PLUGIN_BUILD)\n\nDEPS := $(PLUGIN_OBJS:$(bdir)/%.o=$(bdir)/.%.d)\n\nall: $(PLUGINS)\n\n$(bdir):\n\t@mkdir -p $(bdir)\n\n$(PLUGIN_OBJS): | $(bdir)\n$(DEPS): | $(bdir)\n\n$(PLUGIN_OBJS): $(bdir)/%.o : %.c\n\t$(Q)$(do_compile_plugin_obj)\n\n$(PLUGIN_BUILD): $(bdir)/%.so: $(bdir)/%.o\n\t$(Q)$(do_plugin_build)\n\n$(DEPS): $(bdir)/.%.d: %.c\n\t$(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@\n\n$(PLUGIN_OBJS): $(bdir)/%.o : $(bdir)/.%.d\n\nPLUGINS_INSTALL = $(subst .so,.install,$(PLUGINS))\n\n$(PLUGINS_INSTALL): $(bdir)/%.install : $(bdir)/%.so force\n\t$(Q)$(call do_install_data,$<,$(plugin_tracecmd_dir_SQ))\n\ninstall_plugins: $(PLUGINS_INSTALL)\n\n# The following targets are necessary to trigger a rebuild when\n# $(PLUGIN_DIR_TRACECMD) change. Without them, a full clean build would\n# necessary in order to get the binaries updated.\n\n$(bdir)/tracecmd_plugin_dir: $(bdir) force\n\t$(Q)$(N)$(call update_dir, 'PLUGIN_DIR_TRACECMD=$(PLUGIN_DIR_TRACECMD)')\n\ndep_includes := $(wildcard $(DEPS))\n\nifneq ($(dep_includes),)\n  include $(dep_includes)\nendif\n\nclean:\n\t$(RM) -f $(bdir)/*.a $(bdir)/*.so $(bdir)/*.o $(bdir)/.*.d\\\n\t\t$(bdir)/tracecmd_plugin_dir\n\nforce:\n.PHONY: clean force\n"
  },
  {
    "path": "lib/trace-cmd/test.c",
    "content": "#include <trace-cmd/trace-cmd.h>\n\nint main()\n{\n\ttracecmd_open_head(\"trace.dat\", 0);\n\treturn 0;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-blk-hack.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include \"trace-cmd.h\"\n#include \"trace-local.h\"\n\nstatic const char blk_event_start[] =\n\t\"name: blktrace\\n\"\n\t\"ID: %d\\n\"\n\t\"format:\\n\"\n\t\"\\tfield:unsigned short common_type;\\toffset:0;\\tsize:2;\\n\"\n\t\"\\tfield:unsigned char common_flags;\\toffset:2;\\tsize:1;\\n\"\n\t\"\\tfield:unsigned char common_preempt_count;\\toffset:3;\\tsize:1;\\n\"\n\t\"\\tfield:int common_pid;\\toffset:4;\\tsize:4;\\n\";\n\nstatic const char blk_body[] = \"\\n\"\n\t\"\\tfield:u64 sector;\\toffset:16;\\tsize:8;\\n\"\n\t\"\\tfield:int bytes;\\toffset:24;\\tsize:4;\\n\"\n\t\"\\tfield:int action;\\toffset:28;\\tsize:4;\\n\"\n\t\"\\tfield:int pid;\\toffset:32;\\tsize:4;\\n\"\n\t\"\\tfield:int device;\\toffset:36;\\tsize:4;\\n\"\n\t\"\\tfield:int cpu;\\toffset:40;\\tsize:4;\\n\"\n\t\"\\tfield:short error;\\toffset:44;\\tsize:2;\\n\"\n\t\"\\tfield:short pdu_len;\\toffset:46;\\tsize:2;\\n\"\n\t\"\\tfield:void data;\\toffset:48;\\tsize:0;\\n\"\n\t\"\\n\"\n\t\"print fmt: \\\"%%d\\\", REC->pid\\n\";\n\nint tracecmd_blk_hack(struct tracecmd_input *handle)\n{\n\tstruct tep_handle *pevent;\n\tstruct tep_event *event;\n\tstruct tep_format_field *field;\n\tchar buf[4096]; /* way more than enough! */\n\tint id;\n\tint l;\n\tint r;\n\n\tpevent = tracecmd_get_tep(handle);\n\n\t/*\n\t * Unfortunately, the TRACE_BLK has changed a bit.\n\t * We need to test if various events exist to try\n\t * to guess what event id TRACE_BLK would be.\n\t */\n\n\t/* It was originally behind the \"power\" event */\n\tevent = tep_find_event_by_name(pevent, \"ftrace\", \"power\");\n\tif (event) {\n\t\tid = event->id + 1;\n\t\tgoto found;\n\t}\n\n\t/*\n\t * But the power tracer is now in perf.\n\t * Then it was after kmem_free\n\t */\n\tevent = tep_find_event_by_name(pevent, \"ftrace\", \"kmem_free\");\n\tif (event) {\n\t\tid = event->id + 1;\n\t\tgoto found;\n\t}\n\n\t/*\n\t * But that then went away.\n\t * Currently it should be behind the user stack.\n\t */\n\tevent = tep_find_event_by_name(pevent, \"ftrace\", \"user_stack\");\n\tif (event) {\n\t\tid = event->id + 1;\n\t\tgoto found;\n\t}\n\t/* Give up :( */\n\treturn -1;\n\n found:\n\t/*\n\t * Blk events are not exported in the events directory.\n\t * This is a hack to attempt to create a block event\n\t * that we can read.\n\t *\n\t * We'll make a format file to look like this:\n\t *\n\t * name: blktrace\n\t * ID: 13\n\t * format:\n\t *\tfield:unsigned short common_type;\toffset:0;\tsize:2;\n\t *\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\n\t *\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\n\t *\tfield:int common_pid;\toffset:4;\tsize:4;\n\t *\tfield:int common_lock_depth;\toffset:8;\tsize:4;\n\t *\n\t *\tfield:u64 sector;\toffset:16;\tsize:8;\n\t *\tfield:int bytes;\toffset:32;\tsize:4;\n\t *\tfield:int action;\toffset:36;\tsize:4;\n\t *\tfield:int pid;\toffset:40;\tsize:4;\n\t *\tfield:int device;\toffset:44;\tsize:4;\n\t *\tfield:int cpu;\toffset:48;\tsize:4;\n\t *\tfield:short error;\toffset:52;\tsize:2;\n\t *\tfield:short pdu_len;\toffset:54;\tsize:2;\n\t *\tfield:void data;\toffset:60;\tsize:0;\n\t *\n\t * print fmt: \"%d\", REC->pid\n\t *\n\t * Note: the struct blk_io_trace is used directly and\n\t * just the first parts of the struct are not used in order\n\t * to not write over the ftrace data.\n\t */\n\n\t/* Make sure the common fields exist */\n\tfield = tep_find_common_field(event, \"common_type\");\n\tif (!field || field->offset != 0 || field->size != 2)\n\t\tgoto fail;\n\tfield = tep_find_common_field(event, \"common_flags\");\n\tif (!field || field->offset != 2 || field->size != 1)\n\t\tgoto fail;\n\tfield = tep_find_common_field(event, \"common_preempt_count\");\n\tif (!field || field->offset != 3 || field->size != 1)\n\t\tgoto fail;\n\tfield = tep_find_common_field(event, \"common_pid\");\n\tif (!field || field->offset != 4 || field->size != 4)\n\t\tgoto fail;\n\tr = sprintf(buf, blk_event_start, id);\n\tl = r;\n\n\t/* lock depth is optional */\n\tfield = tep_find_common_field(event, \"common_lock_depth\");\n\tif (field) {\n\t\tif (field->offset != 8 || field->size != 4)\n\t\t\treturn -1;\n\t\tr = sprintf(buf+l, \"\\tfield:int common_lock_depth;\\toffset:8;\\tsize:4;\\n\");\n\t\tl += r;\n\t}\n\n\tr = sprintf(buf+l, blk_body);\n\n\t/* Parse this event */\n\tl += r;\n\ttep_parse_event(pevent, buf, l, \"ftrace\");\n\n\treturn 0;\n\n fail:\n\treturn -1;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-compress-zlib.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2021, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com>\n *\n */\n#include <stdlib.h>\n#include <dlfcn.h>\n#include <zlib.h>\n#include <errno.h>\n\n#include \"trace-cmd-private.h\"\n\n#define __ZLIB_NAME\t\t\"zlib\"\n#define __ZLIB_WEIGTH\t\t10\n\nstatic int zlib_compress(void *ctx, const void *in, int in_bytes, void *out, int out_bytes)\n{\n\tunsigned long obytes = out_bytes;\n\tint ret;\n\n\tret = compress2((unsigned char *)out, &obytes,\n\t\t\t(unsigned char *)in, (unsigned long)in_bytes, Z_BEST_COMPRESSION);\n\tswitch (ret) {\n\tcase Z_OK:\n\t\treturn obytes;\n\tcase Z_BUF_ERROR:\n\t\terrno = -ENOBUFS;\n\t\tbreak;\n\tcase Z_MEM_ERROR:\n\t\terrno = -ENOMEM;\n\t\tbreak;\n\tcase Z_STREAM_ERROR:\n\t\terrno = -EINVAL;\n\t\tbreak;\n\tcase Z_ERRNO:\n\t\tbreak;\n\tdefault:\n\t\terrno = -EFAULT;\n\t\tbreak;\n\t}\n\n\treturn -1;\n}\n\nstatic int zlib_decompress(void *ctx, const void *in, int in_bytes, void *out, int out_bytes)\n{\n\tunsigned long obytes = out_bytes;\n\tint ret;\n\n\tret = uncompress((unsigned char *)out, &obytes,\n\t\t\t (unsigned char *)in, (unsigned long)in_bytes);\n\tswitch (ret) {\n\tcase Z_OK:\n\t\treturn obytes;\n\tcase Z_BUF_ERROR:\n\t\terrno = -ENOBUFS;\n\t\tbreak;\n\tcase Z_MEM_ERROR:\n\t\terrno = -ENOMEM;\n\t\tbreak;\n\tcase Z_DATA_ERROR:\n\t\terrno = -EINVAL;\n\t\tbreak;\n\tcase Z_ERRNO:\n\t\tbreak;\n\tdefault:\n\t\terrno = -EFAULT;\n\t\tbreak;\n\t}\n\n\treturn -1;\n}\n\nstatic unsigned int zlib_compress_bound(void *ctx, unsigned int in_bytes)\n{\n\treturn compressBound(in_bytes);\n}\n\nstatic bool zlib_is_supported(const char *name, const char *version)\n{\n\tconst char *zver;\n\n\tif (!name)\n\t\treturn false;\n\tif (strlen(name) != strlen(__ZLIB_NAME) || strcmp(name, __ZLIB_NAME))\n\t\treturn false;\n\n\tif (!version)\n\t\treturn true;\n\n\tzver = zlibVersion();\n\tif (!zver)\n\t\treturn false;\n\n\t/* Compare the major version number */\n\tif (atoi(version) <= atoi(zver))\n\t\treturn true;\n\n\treturn false;\n}\n\nint tracecmd_zlib_init(void)\n{\n\tstruct tracecmd_compression_proto proto;\n\n\tmemset(&proto, 0, sizeof(proto));\n\tproto.name = __ZLIB_NAME;\n\tproto.version = zlibVersion();\n\tproto.weight = __ZLIB_WEIGTH;\n\tproto.compress = zlib_compress;\n\tproto.uncompress = zlib_decompress;\n\tproto.is_supported = zlib_is_supported;\n\tproto.compress_size = zlib_compress_bound;\n\n\treturn tracecmd_compress_proto_register(&proto);\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-compress-zstd.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2022, Sebastian Andrzej Siewior <sebastian@breakpoint.cc>\n *\n */\n#include <stdlib.h>\n#include <zstd.h>\n#include <errno.h>\n\n#include \"trace-cmd-private.h\"\n\n#define __ZSTD_NAME\t\t\"zstd\"\n#define __ZSTD_WEIGTH\t\t5\n\nstruct zstd_context {\n\tZSTD_CCtx *ctx_c;\n\tZSTD_DCtx *ctx_d;\n};\n\nstatic int zstd_compress(void *ctx, const void *in, int in_bytes, void *out, int out_bytes)\n{\n\tstruct zstd_context *context = ctx;\n\tsize_t ret;\n\n\tif (!ctx)\n\t\treturn -1;\n\n\tret = ZSTD_compress2(context->ctx_c, out, out_bytes, in, in_bytes);\n\tif (ZSTD_isError(ret))\n\t\treturn -1;\n\n\treturn ret;\n}\n\nstatic int zstd_decompress(void *ctx, const void *in, int in_bytes, void *out, int out_bytes)\n{\n\tstruct zstd_context *context = ctx;\n\tsize_t ret;\n\n\tif (!ctx)\n\t\treturn -1;\n\n\tret = ZSTD_decompressDCtx(context->ctx_d, out, out_bytes, in, in_bytes);\n\tif (ZSTD_isError(ret)) {\n\t\terrno = -EINVAL;\n\t\treturn -1;\n\t}\n\n\treturn ret;\n}\n\nstatic unsigned int zstd_compress_bound(void *ctx, unsigned int in_bytes)\n{\n\treturn ZSTD_compressBound(in_bytes);\n}\n\nstatic bool zstd_is_supported(const char *name, const char *version)\n{\n\tif (!name)\n\t\treturn false;\n\tif (strcmp(name, __ZSTD_NAME))\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic void *new_zstd_context(void)\n{\n\tstruct zstd_context *context;\n\tsize_t r;\n\n\tcontext = calloc(1, sizeof(*context));\n\tif (!context)\n\t\treturn NULL;\n\n\tcontext->ctx_c = ZSTD_createCCtx();\n\tcontext->ctx_d = ZSTD_createDCtx();\n\tif (!context->ctx_c || !context->ctx_d)\n\t\tgoto err;\n\n\tr = ZSTD_CCtx_setParameter(context->ctx_c, ZSTD_c_contentSizeFlag, 0);\n\tif (ZSTD_isError(r))\n\t\tgoto err;\n\n\treturn context;\nerr:\n\tZSTD_freeCCtx(context->ctx_c);\n\tZSTD_freeDCtx(context->ctx_d);\n\tfree(context);\n\treturn NULL;\n}\nstatic void free_zstd_context(void *ctx)\n{\n\tstruct zstd_context *context = ctx;\n\n\tif (!ctx)\n\t\treturn;\n\n\tZSTD_freeCCtx(context->ctx_c);\n\tZSTD_freeDCtx(context->ctx_d);\n\tfree(context);\n}\n\nint tracecmd_zstd_init(void)\n{\n\tstruct tracecmd_compression_proto proto;\n\n\tmemset(&proto, 0, sizeof(proto));\n\tproto.name = __ZSTD_NAME;\n\tproto.version = ZSTD_versionString();\n\tproto.weight = __ZSTD_WEIGTH;\n\tproto.compress = zstd_compress;\n\tproto.uncompress = zstd_decompress;\n\tproto.is_supported = zstd_is_supported;\n\tproto.compress_size = zstd_compress_bound;\n\tproto.new_context = new_zstd_context;\n\tproto.free_context = free_zstd_context;\n\n\treturn tracecmd_compress_proto_register(&proto);\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-compress.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2021, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com>\n *\n */\n#include <stdlib.h>\n#include <sys/time.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <limits.h>\n#include <unistd.h>\n\n#include \"trace-cmd-private.h\"\n#include \"trace-cmd-local.h\"\n\nstruct compress_proto {\n\tstruct compress_proto *next;\n\tchar *proto_name;\n\tchar *proto_version;\n\tint weight;\n\n\tint (*compress_block)(void *ctx, const void *in, int in_bytes, void *out, int out_bytes);\n\tint (*uncompress_block)(void *ctx, const void *in,  int in_bytes, void *out, int out_bytes);\n\tunsigned int (*compress_size)(void *ctx, unsigned int bytes);\n\tbool (*is_supported)(const char *name, const char *version);\n\tvoid *(*new_context)(void);\n\tvoid (*free_context)(void *ctx);\n};\n\nstatic struct compress_proto *proto_list;\n\nstruct tracecmd_compression {\n\tint\t\t\t\tfd;\n\tsize_t\t\t\t\tcapacity;\n\tsize_t\t\t\t\tcapacity_read;\n\tsize_t\t\t\t\tpointer;\n\tchar\t\t\t\t*buffer;\n\tstruct compress_proto\t\t*proto;\n\tstruct tep_handle\t\t*tep;\n\tstruct tracecmd_msg_handle\t*msg_handle;\n\tvoid\t\t\t\t*context;\n};\n\nstatic ssize_t read_fd(int fd, char *dst, int len)\n{\n\tsize_t size = 0;\n\tssize_t r;\n\n\tdo {\n\t\tr = read(fd, dst+size, len);\n\t\tif (r > 0) {\n\t\t\tsize += r;\n\t\t\tlen -= r;\n\t\t} else\n\t\t\tbreak;\n\t} while (r > 0);\n\n\tif (len)\n\t\treturn -1;\n\treturn size;\n}\n\nstatic ssize_t  write_fd(int fd, const void *data, size_t size)\n{\n\tssize_t  tot = 0;\n\tssize_t  w;\n\n\tdo {\n\t\tw = write(fd, data + tot, size - tot);\n\t\ttot += w;\n\n\t\tif (!w)\n\t\t\tbreak;\n\t\tif (w < 0)\n\t\t\treturn w;\n\t} while (tot != size);\n\n\treturn tot;\n}\n\nstatic int read_size_val(struct tep_handle *tep, size_t size, int *endian4)\n{\n\tif (size > UINT_MAX)\n\t\treturn -1;\n\n\t*endian4 = size;\n\t*endian4 = tep_read_number(tep, endian4, 4);\n\treturn 0;\n}\n\nstatic ssize_t  do_write(struct tracecmd_compression *handle,\n\t\t\t  const void *data, size_t size)\n{\n\tint ret;\n\n\tif (handle->msg_handle) {\n\t\tret = tracecmd_msg_data_send(handle->msg_handle, data, size);\n\t\tif (ret)\n\t\t\treturn -1;\n\t\treturn size;\n\t}\n\treturn write_fd(handle->fd, data, size);\n}\n\nstatic inline int buffer_extend(struct tracecmd_compression *handle, size_t size)\n{\n\tssize_t  extend;\n\tchar *buf;\n\n\tif (size <= handle->capacity)\n\t\treturn 0;\n\n\textend = (size / BUFSIZ + 1) * BUFSIZ;\n\tbuf = realloc(handle->buffer, extend);\n\tif (!buf)\n\t\treturn -1;\n\thandle->buffer = buf;\n\thandle->capacity = extend;\n\n\treturn 0;\n}\n\n/**\n * tracecmd_compress_lseek - Move the read/write pointer into the compression buffer\n * @handle: compression handle\n * @offset: number of bytes to move the pointer, can be negative or positive\n * @whence: the starting position of the pointer movement,\n *\n * Returns the new file pointer on success, or -1 in case of an error.\n */\noff_t tracecmd_compress_lseek(struct tracecmd_compression *handle, off_t offset, int whence)\n{\n\tunsigned long p;\n\n\tif (!handle)\n\t\treturn (off_t)-1;\n\n\tif (!handle->buffer)\n\t\treturn (whence == SEEK_CUR && offset == 0) ? 0 : (off_t)-1;\n\n\tswitch (whence) {\n\tcase SEEK_CUR:\n\t\tp = handle->pointer + offset;\n\t\tbreak;\n\tcase SEEK_END:\n\t\tp = handle->capacity + offset;\n\t\tbreak;\n\tcase SEEK_SET:\n\t\tp = offset;\n\t\tbreak;\n\tdefault:\n\t\treturn (off_t)-1;\n\t}\n\n\tif (buffer_extend(handle, p))\n\t\treturn (off_t)-1;\n\n\thandle->pointer = p;\n\n\treturn p;\n}\n\nstatic ssize_t compress_read(struct tracecmd_compression *handle, char *dst, size_t len)\n{\n\n\tif (handle->pointer > handle->capacity_read)\n\t\treturn -1;\n\n\tif (handle->pointer + len > handle->capacity_read)\n\t\tlen = handle->capacity_read - handle->pointer;\n\n\tmemcpy(dst, handle->buffer + handle->pointer, len);\n\n\treturn len;\n}\n\n/**\n * tracecmd_compress_pread - pread() on compression buffer\n * @handle: compression handle\n * @dst: return, store the read data\n * @len: length of data to be read\n * @offset: offset in the buffer of data to be read\n *\n * Read a @len of data from the compression buffer at given @offset,\n * without updating the buffer pointer.\n *\n * On success returns the number of bytes read, or -1 on failure.\n */\nssize_t tracecmd_compress_pread(struct tracecmd_compression *handle, char *dst, size_t len, off_t offset)\n{\n\tssize_t ret;\n\n\tif (!handle || !handle->buffer || offset > handle->capacity_read)\n\t\treturn -1;\n\n\tret = tracecmd_compress_lseek(handle, offset, SEEK_SET);\n\tif (ret < 0)\n\t\treturn ret;\n\treturn compress_read(handle, dst, len);\n}\n\n/**\n * tracecmd_compress_buffer_read - read() from compression buffer\n * @handle: compression handle\n * @dst: return, store the read data\n * @len: length of data to be read\n *\n * Read a @len of data from the compression buffer\n *\n * On success returns the number of bytes read, or -1 on failure.\n */\nssize_t tracecmd_compress_buffer_read(struct tracecmd_compression *handle, char *dst, size_t len)\n{\n\tssize_t ret;\n\n\tif (!handle || !handle->buffer)\n\t\treturn -1;\n\n\tret = compress_read(handle, dst, len);\n\tif (ret > 0)\n\t\thandle->pointer += ret;\n\n\treturn ret;\n}\n\n/**\n * tracecmd_compress_reset - Reset the compression buffer\n * @handle: compression handle\n *\n * Reset the compression buffer, any data currently in the buffer\n * will be destroyed.\n *\n */\nvoid tracecmd_compress_reset(struct tracecmd_compression *handle)\n{\n\tif (!handle)\n\t\treturn;\n\n\tfree(handle->buffer);\n\thandle->buffer = NULL;\n\thandle->pointer = 0;\n\thandle->capacity_read = 0;\n\thandle->capacity = 0;\n}\n\n/**\n * tracecmd_uncompress_block - uncompress a memory block\n * @handle: compression handle\n *\n * Read compressed memory block from the file and uncompress it into\n * internal buffer. The tracecmd_compress_buffer_read() can be used\n * to read the uncompressed data from the buffer.\n *\n * Returns 0 on success, or -1 in case of an error.\n */\nint tracecmd_uncompress_block(struct tracecmd_compression *handle)\n{\n\tunsigned int s_uncompressed;\n\tunsigned int s_compressed;\n\tchar *bytes = NULL;\n\tchar buf[4];\n\tint size;\n\tint ret;\n\n\tif (!handle || !handle->proto || !handle->proto->uncompress_block)\n\t\treturn -1;\n\n\ttracecmd_compress_reset(handle);\n\n\tif (read(handle->fd, buf, 4) != 4)\n\t\treturn -1;\n\n\ts_compressed = tep_read_number(handle->tep, buf, 4);\n\tif (read(handle->fd, buf, 4) != 4)\n\t\treturn -1;\n\n\ts_uncompressed = tep_read_number(handle->tep, buf, 4);\n\tsize = s_uncompressed > s_compressed ? s_uncompressed : s_compressed;\n\n\thandle->buffer = malloc(size);\n\tif (!handle->buffer)\n\t\treturn -1;\n\n\tbytes = malloc(s_compressed);\n\tif (!bytes)\n\t\tgoto error;\n\n\tif (read_fd(handle->fd, bytes, s_compressed) < 0)\n\t\tgoto error;\n\n\tret = handle->proto->uncompress_block(handle->context,\n\t\t\t\t\t      bytes, s_compressed, handle->buffer, size);\n\tif (ret < 0)\n\t\tgoto error;\n\n\tfree(bytes);\n\thandle->pointer = 0;\n\thandle->capacity_read = ret;\n\thandle->capacity = size;\n\treturn 0;\nerror:\n\ttracecmd_compress_reset(handle);\n\tfree(bytes);\n\treturn -1;\n}\n\n/**\n * tracecmd_compress_block - compress a memory block\n * @handle: compression handle\n *\n * Compress the content of the internal memory buffer and write\n * the compressed data in the file. The tracecmd_compress_buffer_write()\n * can be used to write data into the internal memory buffer,\n * before calling this API.\n *\n * Returns 0 on success, or -1 in case of an error.\n */\nint tracecmd_compress_block(struct tracecmd_compression *handle)\n{\n\tunsigned int size, real_size;\n\tchar *buf;\n\tint endian4;\n\tint ret;\n\n\tif (!handle || !handle->proto ||\n\t    !handle->proto->compress_size || !handle->proto->compress_block)\n\t\treturn -1;\n\n\tsize = handle->proto->compress_size(handle->context, handle->pointer);\n\n\tbuf = malloc(size);\n\tif (!buf)\n\t\treturn -1;\n\n\treal_size = handle->proto->compress_block(handle->context, handle->buffer, handle->pointer, buf, size);\n\tif (real_size < 0) {\n\t\tret = real_size;\n\t\tgoto out;\n\t}\n\n\t/* Write compressed data size */\n\tendian4 = tep_read_number(handle->tep, &real_size, 4);\n\tret = do_write(handle, &endian4, 4);\n\tif (ret != 4)\n\t\tgoto out;\n\n\t/* Write uncompressed data size */\n\tif (read_size_val(handle->tep, handle->pointer, &endian4) < 0)\n\t\tgoto out;\n\tret = do_write(handle, &endian4, 4);\n\tif (ret != 4) {\n\t\tret = -1;\n\t\tgoto out;\n\t}\n\n\t/* Write compressed data */\n\tret = do_write(handle, buf, real_size);\n\tif (ret != real_size) {\n\t\tret = -1;\n\t\tgoto out;\n\t}\n\n\tret = 0;\n\ttracecmd_compress_reset(handle);\nout:\n\tfree(buf);\n\treturn ret;\n}\n/**\n * tracecmd_uncompress_buffer - Returned the current uncompressed buffer\n * @handle: compression handle\n * @size: A pointer to place the size of the uncompressed buffer in\n *\n * Returns the current uncompressed buffer and resets the compression\n * @handle.\n *\n * Returns the buffer (must be freed with free()) or NULL.\n *  @size (if not NULL) will contain the size of the uncompressed buffer.\n */\nvoid *tracecmd_uncompress_buffer(struct tracecmd_compression *handle, size_t *size)\n{\n\tvoid *buffer;\n\n\tif (!handle)\n\t\treturn NULL;\n\n\tif (size)\n\t\t*size = handle->capacity;\n\n\tbuffer = handle->buffer;\n\thandle->buffer = NULL;\n\ttracecmd_compress_reset(handle);\n\treturn buffer;\n}\n\n/**\n * tracecmd_compress_buffer_write - write() to compression buffer\n * @handle: compression handle\n * @data: data to be written\n * @size: size of @data\n *\n * Write @data of @size in the compression buffer\n *\n * Returns 0 on success, or -1 on failure.\n */\nint tracecmd_compress_buffer_write(struct tracecmd_compression *handle,\n\t\t\t\t   const void *data, size_t size)\n{\n\tif (!handle)\n\t\treturn -1;\n\n\tif (buffer_extend(handle, handle->pointer + size))\n\t\treturn -1;\n\n\tmemcpy(&handle->buffer[handle->pointer], data, size);\n\thandle->pointer += size;\n\tif (handle->capacity_read < handle->pointer)\n\t\thandle->capacity_read = handle->pointer;\n\n\treturn 0;\n}\n\n/**\n * tracecmd_compress_init - initialize the library with available compression algorithms\n */\nvoid tracecmd_compress_init(void)\n{\n\tstruct timeval time;\n\n\tgettimeofday(&time, NULL);\n\tsrand((time.tv_sec * 1000) + (time.tv_usec / 1000));\n\n#ifdef HAVE_ZLIB\n\ttracecmd_zlib_init();\n#endif\n\ttracecmd_zstd_init();\n}\n\nstatic struct compress_proto *compress_proto_select(void)\n{\n\tstruct compress_proto *proto = proto_list;\n\tstruct compress_proto *selected = NULL;\n\n\twhile (proto) {\n\t\tif (!selected || selected->weight > proto->weight)\n\t\t\tselected = proto;\n\t\tproto = proto->next;\n\t}\n\n\treturn selected;\n}\n\n/**\n * tracecmd_compress_alloc - Allocate a new compression context\n * @name: name of the compression algorithm.\n *        If NULL - auto select the best available algorithm\n * @version: version of the compression algorithm, can be NULL\n * @fd: file descriptor for reading / writing data\n * @tep: tep handle, used to encode the data\n * @msg_handle: message handle, use it for reading / writing data instead of @fd\n *\n * Returns NULL on failure or pointer to allocated compression context.\n * The returned context must be freed by tracecmd_compress_destroy()\n */\nstruct tracecmd_compression *tracecmd_compress_alloc(const char *name, const char *version,\n\t\t\t\t\t\t     int fd, struct tep_handle *tep,\n\t\t\t\t\t\t     struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_compression *new;\n\tstruct compress_proto *proto;\n\n\tif (name) {\n\t\tproto = proto_list;\n\t\twhile (proto) {\n\t\t\tif (proto->is_supported && proto->is_supported(name, version))\n\t\t\t\tbreak;\n\t\t\tproto = proto->next;\n\t\t}\n\t} else {\n\t\tproto = compress_proto_select();\n\t}\n\tif (!proto)\n\t\treturn NULL;\n\n\tnew = calloc(1, sizeof(*new));\n\tif (!new)\n\t\treturn NULL;\n\n\tnew->fd = fd;\n\tnew->tep = tep;\n\tnew->msg_handle = msg_handle;\n\tnew->proto = proto;\n\tif (proto->new_context)\n\t\tnew->context = proto->new_context();\n\n\treturn new;\n}\n\n/**\n * tracecmd_compress_destroy - Free a compression context\n * @handle: handle to the compression context that will be freed\n */\nvoid tracecmd_compress_destroy(struct tracecmd_compression *handle)\n{\n\tif (!handle)\n\t\treturn;\n\n\ttracecmd_compress_reset(handle);\n\n\tif (handle->proto && handle->proto->free_context)\n\t\thandle->proto->free_context(handle->context);\n\n\tfree(handle);\n}\n\n/**\n * tracecmd_compress_is_supported - check if compression algorithm is supported\n * @name: name of the compression algorithm.\n * @version: version of the compression algorithm.\n *\n * Checks if compression algorithm with given name and version is supported.\n * Returns true if the algorithm is supported or false if it is not.\n */\nbool tracecmd_compress_is_supported(const char *name, const char *version)\n{\n\tstruct compress_proto *proto = proto_list;\n\n\tif (!name)\n\t\treturn NULL;\n\n\twhile (proto) {\n\t\tif (proto->is_supported && proto->is_supported(name, version))\n\t\t\treturn true;\n\t\tproto = proto->next;\n\t}\n\treturn false;\n}\n\n/**\n * tracecmd_compress_proto_get_name - get name and version of compression algorithm\n * @compress: compression handle.\n * @name: return, name of the compression algorithm.\n * @version: return, version of the compression algorithm.\n *\n * Returns 0 on success, or -1 in case of an error. If 0 is returned, the name\n * and version of the algorithm are stored in @name and @version. The returned\n * strings must *not* be freed.\n */\nint tracecmd_compress_proto_get_name(struct tracecmd_compression *compress,\n\t\t\t\t     const char **name, const char **version)\n{\n\tif (!compress || !compress->proto)\n\t\treturn -1;\n\n\tif (name)\n\t\t*name = compress->proto->proto_name;\n\tif (version)\n\t\t*version = compress->proto->proto_version;\n\n\treturn 0;\n}\n\n/**\n * tracecmd_compress_proto_register - register a new compression algorithm\n * @name: name of the compression algorithm.\n * @version: version of the compression algorithm.\n * @weight: weight of the compression algorithm, lower is better.\n * @compress: compression hook, called to compress a memory block.\n * @uncompress: uncompression hook, called to uncompress a memory block.\n * @compress_size: hook, called to get the required minimum size of the buffer\n *\t\t   for compression given number of bytes.\n * @is_supported: check hook, called to check if compression with given name and\n *\t\t   version is supported by this plugin.\n *\n * Returns 0 on success, or -1 in case of an error. If algorithm with given name\n * and version is already registered, -1 is returned.\n */\nint tracecmd_compress_proto_register(struct tracecmd_compression_proto *proto)\n{\n\tstruct compress_proto *new;\n\n\tif (!proto || !proto->name || !proto->compress || !proto->uncompress)\n\t\treturn -1;\n\n\tif (tracecmd_compress_is_supported(proto->name, proto->version))\n\t\treturn -1;\n\n\tnew = calloc(1, sizeof(*new));\n\tif (!new)\n\t\treturn -1;\n\n\tnew->proto_name = strdup(proto->name);\n\tif (!new->proto_name)\n\t\tgoto error;\n\n\tnew->proto_version = strdup(proto->version);\n\tif (!new->proto_version)\n\t\tgoto error;\n\n\tnew->compress_block = proto->compress;\n\tnew->uncompress_block = proto->uncompress;\n\tnew->compress_size = proto->compress_size;\n\tnew->is_supported = proto->is_supported;\n\tnew->weight = proto->weight;\n\tnew->next = proto_list;\n\tnew->new_context = proto->new_context;\n\tnew->free_context = proto->free_context;\n\tproto_list = new;\n\treturn 0;\n\nerror:\n\tfree(new->proto_name);\n\tfree(new->proto_version);\n\tfree(new);\n\treturn -1;\n}\n\n/**\n * tracecmd_compress_free - free the library resources, related to available compression algorithms\n *\n */\nvoid tracecmd_compress_free(void)\n{\n\tstruct compress_proto *proto = proto_list;\n\tstruct compress_proto *del;\n\n\twhile (proto) {\n\t\tdel = proto;\n\t\tproto = proto->next;\n\t\tfree(del->proto_name);\n\t\tfree(del->proto_version);\n\t\tfree(del);\n\t}\n\tproto_list = NULL;\n}\n\n/**\n * tracecmd_compress_protos_get - get a list of all supported compression algorithms and versions\n * @names: return, array with names of all supported compression algorithms\n * @versions: return, array with versions of all supported compression algorithms\n *\n * On success, the size of @names and @versions arrays is returned.\n * Those arrays are allocated by the API and must be freed with free() by the\n * caller. Both arrays are with same size, each name from @names corresponds to\n * a version from @versions. The last element in both arrays is a NULL pointer.\n * On error -1 is returned and @names and @versions arrays are not allocated.\n */\nint tracecmd_compress_protos_get(char ***names, char ***versions)\n{\n\tstruct compress_proto *proto = proto_list;\n\tchar **n = NULL;\n\tchar **v = NULL;\n\tint c, i;\n\n\tfor (c = 0; proto; proto = proto->next)\n\t\tc++;\n\n\tif (c < 1)\n\t\treturn c;\n\n\tn = calloc(c + 1, sizeof(char *));\n\tif (!n)\n\t\tgoto error;\n\tv = calloc(c + 1, sizeof(char *));\n\tif (!v)\n\t\tgoto error;\n\n\tproto = proto_list;\n\tfor (i = 0; i < c && proto; i++) {\n\t\tn[i] = proto->proto_name;\n\t\tv[i] = proto->proto_version;\n\t\tproto = proto->next;\n\t}\n\n\tn[i] = NULL;\n\tv[i] = NULL;\n\t*names = n;\n\t*versions = v;\n\treturn c;\n\nerror:\n\tfree(n);\n\tfree(v);\n\treturn -1;\n}\n\n/**\n * tracecmd_compress_copy_from - Copy and compress data from a file\n * @handle: compression handle\n * @fd: file descriptor to uncompressed data to copy from\n * @chunk_size: size of one compression chunk\n * @read_size: Pointer to max bytes to read from. The pointer is updated\n *\t       with the actual size of compressed data read. If 0 is passed,\n *\t       read until the EOF is reached.\n * @write_size: return, size of the compressed data written into @handle\n *\n * This function reads uncompressed data from given @fd, compresses the data\n * using the @handle compression context and writes the compressed data into the\n * fd associated with the @handle. The data is compressed on chunks with given\n * @chunk_size size. The compressed data is written in the format:\n *  - 4 bytes, chunks count\n *  - for each chunk:\n *    - 4 bytes, size of compressed data in this chunk\n *    - 4 bytes, uncompressed size of the data in this chunk\n *    - data, bytes of <size of compressed data in this chunk>\n *\n * On success 0 is returned, @read_size and @write_size are updated with the size of\n * read and written data.\n */\nint tracecmd_compress_copy_from(struct tracecmd_compression *handle, int fd, int chunk_size,\n\t\t\t\tsize_t *read_size, size_t *write_size)\n{\n\tsize_t rchunk = 0;\n\tsize_t chunks = 0;\n\tsize_t rsize = 0;\n\tsize_t rmax = 0;\n\tsize_t csize;\n\tsize_t size;\n\tsize_t all;\n\tsize_t r;\n\toff_t end_offset;\n\toff_t offset;\n\tchar *buf_from;\n\tchar *buf_to;\n\tint endian4;\n\tint ret;\n\n\tif (!handle || !handle->proto ||\n\t    !handle->proto->compress_block || !handle->proto->compress_size)\n\t\treturn 0;\n\n\tif (read_size)\n\t\trmax = *read_size;\n\tcsize = handle->proto->compress_size(handle->context, chunk_size);\n\tbuf_from = malloc(chunk_size);\n\tif (!buf_from)\n\t\treturn -1;\n\n\tbuf_to = malloc(csize);\n\tif (!buf_to) {\n\t\tfree(buf_from);\n\t\treturn -1;\n\t}\n\n\t/* save the initial offset and write 0 as initial chunk count */\n\toffset = lseek(handle->fd, 0, SEEK_CUR);\n\tif (write_fd(handle->fd, &chunks, 4) != 4)\n\t\treturn -1;\n\n\tdo {\n\t\tall = 0;\n\t\tif (rmax > 0 && (rmax - rsize) < chunk_size)\n\t\t\trchunk = (rmax - rsize);\n\t\telse\n\t\t\trchunk = chunk_size;\n\n\t\tdo {\n\t\t\tr = read(fd, buf_from + all, rchunk - all);\n\t\t\tif (r <= 0)\n\t\t\t\tbreak;\n\n\t\t\tall += r;\n\t\t} while (all != rchunk);\n\n\n\t\tif (r < 0 || (rmax > 0 && rsize >= rmax))\n\t\t\tbreak;\n\t\trsize += all;\n\t\tsize = csize;\n\t\tif (all > 0) {\n\t\t\tret = handle->proto->compress_block(handle->context,\n\t\t\t\t\t\t\t    buf_from, all, buf_to, size);\n\t\t\tif (ret < 0) {\n\t\t\t\tif (errno == EINTR)\n\t\t\t\t\tcontinue;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsize = ret;\n\t\t\t/* Write compressed data size */\n\t\t\tif (read_size_val(handle->tep, size, &endian4) < 0)\n\t\t\t\tbreak;\n\t\t\tret = write_fd(handle->fd, &endian4, 4);\n\t\t\tif (ret != 4)\n\t\t\t\tbreak;\n\n\t\t\t/* Write uncompressed data size */\n\t\t\tif (read_size_val(handle->tep, all, &endian4) < 0)\n\t\t\t\tbreak;\n\t\t\tret = write_fd(handle->fd, &endian4, 4);\n\t\t\tif (ret != 4)\n\t\t\t\tbreak;\n\n\t\t\t/* Write the compressed data */\n\t\t\tret = write_fd(handle->fd, buf_to, size);\n\t\t\tif (ret != size)\n\t\t\t\tbreak;\n\t\t\tchunks++;\n\t\t}\n\t} while (all > 0);\n\n\tfree(buf_from);\n\tfree(buf_to);\n\n\tif (all)\n\t\treturn -1;\n\n\tif (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\tif (read_size_val(handle->tep, chunks, &endian4) < 0)\n\t\treturn -1;\n\t/* write chunks count*/\n\tif (write_fd(handle->fd, &endian4, 4) != 4)\n\t\treturn -1;\n\tend_offset = lseek(handle->fd, 0, SEEK_END);\n\tif (end_offset == (off_t)-1)\n\t\treturn -1;\n\n\tif (read_size)\n\t\t*read_size = rsize;\n\tif (write_size)\n\t\t*write_size = end_offset - offset;\n\treturn 0;\n}\n\n/**\n * tracecmd_load_chunks_info - Read compression chunks information from the file\n * @handle: compression handle\n * @chunks_info: return, array with compression chunks information\n *\n * This function reads information of all compression chunks in the current\n * compression block from the file and fills that information in a newly\n * allocated array @chunks_info which is returned.\n *\n * On success count of compression chunks is returned. Array of that count is\n * allocated and returned in @chunks_info. Each entry describes one compression\n * chunk. On error -1 is returned. In case of success, @chunks_info must be\n * freed by free().\n */\nint tracecmd_load_chunks_info(struct tracecmd_compression *handle,\n\t\t\t      struct tracecmd_compress_chunk **chunks_info)\n{\n\tstruct tracecmd_compress_chunk *chunks = NULL;\n\tsize_t size = 0;\n\tunsigned int count = 0;\n\toff_t offset;\n\tint ret = -1;\n\tchar buf[4];\n\tint i;\n\n\tif (!handle)\n\t\treturn -1;\n\n\toffset = lseek(handle->fd, 0, SEEK_CUR);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\tif (read(handle->fd, buf, 4) != 4)\n\t\treturn -1;\n\n\tcount = tep_read_number(handle->tep, buf, 4);\n\tif (!count) {\n\t\tret = 0;\n\t\tgoto out;\n\t}\n\n\tchunks = calloc(count, sizeof(struct tracecmd_compress_chunk));\n\tif (!chunks)\n\t\tgoto out;\n\n\tfor (i = 0; i < count; i++) {\n\t\tchunks[i].zoffset = lseek(handle->fd, 0, SEEK_CUR);\n\t\tif (chunks[i].zoffset == (off_t)-1)\n\t\t\tgoto out;\n\t\tif (read(handle->fd, buf, 4) != 4)\n\t\t\tgoto out;\n\t\tchunks[i].zsize = tep_read_number(handle->tep, buf, 4);\n\t\tchunks[i].offset = size;\n\t\tif (read(handle->fd, buf, 4) != 4)\n\t\t\tgoto out;\n\t\tchunks[i].size = tep_read_number(handle->tep, buf, 4);\n\t\tsize += chunks[i].size;\n\t\tif (lseek(handle->fd, chunks[i].zsize, SEEK_CUR) == (off_t)-1)\n\t\t\tgoto out;\n\t}\n\n\tret = count;\nout:\n\tif (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)\n\t\tret = -1;\n\n\tif (ret > 0 && chunks_info)\n\t\t*chunks_info = chunks;\n\telse\n\t\tfree(chunks);\n\n\treturn ret;\n}\n\n/**\n * tracecmd_uncompress_chunk - Uncompress given compression chunk.\n * @handle: compression handle\n * @chunk: chunk, that will be uncompressed in @data\n * @data: Preallocated memory for uncompressed data. Must have enough space\n * to hold the uncompressed data.\n *\n * This function uncompresses the chunk described by @chunk and stores\n * the uncompressed data in the preallocated memory @data.\n *\n * On success 0 is returned and the uncompressed data is stored in @data.\n * On error -1 is returned.\n */\nint tracecmd_uncompress_chunk(struct tracecmd_compression *handle,\n\t\t\t      struct tracecmd_compress_chunk *chunk, char *data)\n{\n\tchar *bytes_in = NULL;\n\tint ret = -1;\n\n\tif (!handle || !handle->proto || !handle->proto->uncompress_block || !chunk || !data)\n\t\treturn -1;\n\n\tif (lseek(handle->fd, chunk->zoffset + 8, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\tbytes_in = malloc(chunk->zsize);\n\tif (!bytes_in)\n\t\treturn -1;\n\n\tif (read_fd(handle->fd, bytes_in, chunk->zsize) < 0)\n\t\tgoto out;\n\n\tif (handle->proto->uncompress_block(handle->context,\n\t\t\t\t\t    bytes_in, chunk->zsize, data, chunk->size) < 0)\n\t\tgoto out;\n\n\tret = 0;\nout:\n\tfree(bytes_in);\n\treturn ret;\n}\n\n/**\n * tracecmd_uncompress_copy_to - Uncompress data and copy to a file\n * @handle: compression handle\n * @fd: file descriptor to uncompressed data to copy into\n * @read_size: return, size of the compressed data read from @handle\n * @write_size: return, size of the uncompressed data written into @fd\n *\n * This function reads compressed data from the fd, associated with @handle,\n * uncompresses it using the @handle compression context and writes\n * the uncompressed data into the fd. The compressed data must be in the format:\n *  - 4 bytes, chunks count\n *  - for each chunk:\n *    - 4 bytes, size of compressed data in this chunk\n *    - 4 bytes, uncompressed size of the data in this chunk\n *    - data, bytes of <size of compressed data in this chunk>\n *\n * On success 0 is returned, @read_size and @write_size are updated with\n * the size of read and written data.\n */\nint tracecmd_uncompress_copy_to(struct tracecmd_compression *handle, int fd,\n\t\t\t\tsize_t *read_size, size_t *write_size)\n{\n\tsize_t s_uncompressed;\n\tsize_t s_compressed;\n\tsize_t rsize = 0;\n\tsize_t wsize = 0;\n\tchar *bytes_out = NULL;\n\tchar *bytes_in = NULL;\n\tint size_out = 0;\n\tint size_in = 0;\n\tint chunks;\n\tchar buf[4];\n\tchar *tmp;\n\tint ret;\n\n\tif (!handle || !handle->proto || !handle->proto->uncompress_block)\n\t\treturn -1;\n\n\tif (read(handle->fd, buf, 4) != 4)\n\t\treturn -1;\n\n\tchunks = tep_read_number(handle->tep, buf, 4);\n\trsize += 4;\n\n\twhile (chunks) {\n\t\tif (read(handle->fd, buf, 4) != 4)\n\t\t\tbreak;\n\n\t\ts_compressed = tep_read_number(handle->tep, buf, 4);\n\t\trsize += 4;\n\t\tif (read(handle->fd, buf, 4) != 4)\n\t\t\tbreak;\n\n\t\ts_uncompressed = tep_read_number(handle->tep, buf, 4);\n\t\trsize += 4;\n\t\tif (!bytes_in || size_in < s_compressed) {\n\t\t\ttmp = realloc(bytes_in, s_compressed);\n\t\t\tif (!tmp)\n\t\t\t\tbreak;\n\n\t\t\tbytes_in = tmp;\n\t\t\tsize_in = s_compressed;\n\t\t}\n\n\t\tif (!bytes_out || size_out < s_uncompressed) {\n\t\t\ttmp = realloc(bytes_out, s_uncompressed);\n\t\t\tif (!tmp)\n\t\t\t\tbreak;\n\t\t\tbytes_out = tmp;\n\t\t\tsize_out = s_uncompressed;\n\t\t}\n\n\t\tif (read_fd(handle->fd, bytes_in, s_compressed) < 0)\n\t\t\tbreak;\n\n\t\trsize += s_compressed;\n\t\tret = handle->proto->uncompress_block(handle->context, bytes_in, s_compressed,\n\t\t\t\t\t\t      bytes_out, s_uncompressed);\n\t\tif (ret < 0)\n\t\t\tbreak;\n\n\t\tif (write_fd(fd, bytes_out, ret) != ret)\n\t\t\tbreak;\n\t\twsize += ret;\n\t\tchunks--;\n\t}\n\tfree(bytes_in);\n\tfree(bytes_out);\n\tif (chunks)\n\t\treturn -1;\n\n\tif (read_size)\n\t\t*read_size = rsize;\n\tif (write_size)\n\t\t*write_size = wsize;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-filter-hash.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>\n * Copyright (C) 2018 VMware Inc, Steven Rostedt <rostedt@goodmis.org>\n *\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <assert.h>\n\n#include \"trace-filter-hash.h\"\n\n#define FILTER_HASH_BITS\t8\n#define FILTER_HASH_SIZE\t(1 << FILTER_HASH_BITS)\n\nstruct tracecmd_filter_id_item *\ntracecmd_filter_id_find(struct tracecmd_filter_id *hash, int id)\n{\n\tint key = tracecmd_quick_hash(id, FILTER_HASH_BITS);\n\tstruct tracecmd_filter_id_item *item = hash->hash[key];\n\n\twhile (item) {\n\t\tif (item->id == id)\n\t\t\tbreak;\n\t\titem = item->next;\n\t}\n\n\treturn item;\n}\n\nvoid tracecmd_filter_id_add(struct tracecmd_filter_id *hash, int id)\n{\n\tint key = tracecmd_quick_hash(id, FILTER_HASH_BITS);\n\tstruct tracecmd_filter_id_item *item;\n\n\titem = calloc(1, sizeof(*item));\n\tassert(item);\n\n\titem->id = id;\n\titem->next = hash->hash[key];\n\thash->hash[key] = item;\n\n\thash->count++;\n}\n\nvoid tracecmd_filter_id_remove(struct tracecmd_filter_id *hash, int id)\n{\n\tint key = tracecmd_quick_hash(id, FILTER_HASH_BITS);\n\tstruct tracecmd_filter_id_item **next = &hash->hash[key];\n\tstruct tracecmd_filter_id_item *item;\n\n\twhile (*next) {\n\t\tif ((*next)->id == id)\n\t\t\tbreak;\n\t\tnext = &(*next)->next;\n\t}\n\n\tif (!*next)\n\t\treturn;\n\n\tassert(hash->count);\n\thash->count--;\n\n\titem = *next;\n\n\t*next = item->next;\n\n\tfree(item);\n}\n\nvoid tracecmd_filter_id_clear(struct tracecmd_filter_id *hash)\n{\n\tstruct tracecmd_filter_id_item *item, *next;\n\tint i;\n\n\tfor (i = 0; i < FILTER_HASH_SIZE; i++) {\n\t\tnext = hash->hash[i];\n\t\tif (!next)\n\t\t\tcontinue;\n\n\t\thash->hash[i] = NULL;\n\t\twhile (next) {\n\t\t\titem = next;\n\t\t\tnext = item->next;\n\t\t\tfree(item);\n\t\t}\n\t}\n\n\thash->count = 0;\n}\n\nstruct tracecmd_filter_id *tracecmd_filter_id_hash_alloc(void)\n{\n\tstruct tracecmd_filter_id *hash;\n\n\thash = calloc(1, sizeof(*hash));\n\tassert(hash);\n\thash->hash = calloc(FILTER_HASH_SIZE, sizeof(*hash->hash));\n\thash->count = 0;\n\n\treturn hash;\n}\n\nvoid tracecmd_filter_id_hash_free(struct tracecmd_filter_id *hash)\n{\n\tif (!hash)\n\t\treturn;\n\n\ttracecmd_filter_id_clear(hash);\n\tfree(hash->hash);\n\tfree(hash);\n}\n\nstruct tracecmd_filter_id *\ntracecmd_filter_id_hash_copy(struct tracecmd_filter_id *hash)\n{\n\tstruct tracecmd_filter_id *new_hash;\n\tstruct tracecmd_filter_id_item *item, **pitem;\n\tint i;\n\n\tif (!hash)\n\t\treturn NULL;\n\n\tnew_hash = tracecmd_filter_id_hash_alloc();\n\tassert(new_hash);\n\n\tfor (i = 0; i < FILTER_HASH_SIZE; i++) {\n\t\titem = hash->hash[i];\n\t\tif (!item)\n\t\t\tcontinue;\n\n\t\tpitem = &new_hash->hash[i];\n\n\t\twhile (item) {\n\t\t\t*pitem = calloc(1, sizeof(*item));\n\t\t\tassert(*pitem);\n\t\t\t**pitem = *item;\n\n\t\t\tpitem = &(*pitem)->next;\n\t\t\titem = item->next;\n\t\t}\n\t}\n\n\tnew_hash->count = hash->count;\n\treturn new_hash;\n}\n\nint *tracecmd_filter_ids(struct tracecmd_filter_id *hash)\n{\n\tstruct tracecmd_filter_id_item *item;\n\tint *ids;\n\tint count = 0;\n\tint i;\n\n\tif (!hash->count)\n\t\treturn NULL;\n\n\tids = malloc(sizeof(*ids) * (hash->count + 1));\n\tif (!ids)\n\t\treturn NULL;\n\n\tfor (i = 0; i < FILTER_HASH_SIZE; i++) {\n\t\titem = hash->hash[i];\n\t\twhile (item) {\n\t\t\tids[count++] = item->id;\n\t\t\titem = item->next;\n\t\t}\n\t}\n\n\tids[count] = -1;\n\treturn ids;\n}\n\n/**\n * filter_id_compare - compare two id hashes to see if they are equal\n * @hash1: one hash to compare\n * @hash2: another hash to compare to @hash1\n *\n * Returns 1 if the two hashes are the same, 0 otherwise.\n */\nint tracecmd_filter_id_compare(struct tracecmd_filter_id *hash1,\n\t\t\t       struct tracecmd_filter_id *hash2)\n{\n\tint *ids;\n\tint ret = 0;\n\tint i;\n\n\t/* If counts don't match, then they obviously are not the same */\n\tif (hash1->count != hash2->count)\n\t\treturn 0;\n\n\t/* If both hashes are empty, they are the same */\n\tif (!hash1->count && !hash2->count)\n\t\treturn 1;\n\n\t/* Now compare the pids of one hash with the other */\n\tids = tracecmd_filter_ids(hash1);\n\tfor (i = 0; ids[i] >= 0; i++) {\n\t\tif (!tracecmd_filter_id_find(hash2, ids[i]))\n\t\t\tbreak;\n\t}\n\n\tif (ids[i] == -1)\n\t\tret = 1;\n\n\tfree(ids);\n\n\treturn ret;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-filter.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2022, Google Inc, Steven Rostedt <rostedt@goodmis.org>\n*/\n#include <stdlib.h>\n#include <trace-cmd.h>\n#include <trace-cmd-local.h>\n\nstruct filter {\n\tstruct tep_event_filter\t\t*filter;\n};\n\nstruct tracecmd_filter {\n\tstruct tep_handle\t*tep;\n\tstruct filter\t\t**event_filters;\n\tstruct filter\t\t**event_notrace;\n\tbool\t\t\t*last_printed;\n\tint\t\t\tnr_cpus;\n\tint\t\t\tnr_filters;\n\tint\t\t\tnr_notrace;\n\tint\t\t\tkernel_stacktrace_id;\n\tint\t\t\tuser_stacktrace_id;\n};\n\nstatic bool test_stacktrace(struct tracecmd_filter *filter, struct tep_record *record,\n\t\t\t    int stacktrace_id)\n{\n\tstruct tep_handle *tep = filter->tep;\n\tint id;\n\n\tif (stacktrace_id < 0)\n\t\treturn false;\n\n\tid = tep_data_type(tep, record);\n\tif (id != stacktrace_id)\n\t\treturn false;\n\n\treturn filter->last_printed[record->cpu];\n}\n\nstatic bool test_stacktraces(struct tracecmd_filter *filter, struct tep_record *record)\n{\n\treturn test_stacktrace(filter, record, filter->kernel_stacktrace_id) ||\n\t\ttest_stacktrace(filter, record, filter->user_stacktrace_id);\n}\n\n__hidden enum tracecmd_filters\ntcmd_filter_match(struct tracecmd_filter *filter, struct tep_record *record)\n{\n\tbool is_stack = false;\n\tbool found = false;\n\tint ret;\n\tint i;\n\n\tif (!filter)\n\t\treturn TRACECMD_FILTER_NONE;\n\n\t/* Setup stack traces. If a event is shown, still show stack traces */\n\tif (!filter->kernel_stacktrace_id) {\n\t\tstruct tep_handle *tep = filter->tep;\n\t\tstruct tep_event *event;\n\n\t\t/* In case the below logic fails, do not do this again */\n\t\tfilter->kernel_stacktrace_id = -1;\n\n\t\tevent = tep_find_event_by_name(tep, \"ftrace\", \"kernel_stack\");\n\t\tif (event)\n\t\t\tfilter->kernel_stacktrace_id = event->id;\n\n\t\tevent = tep_find_event_by_name(tep, \"ftrace\", \"user_stack\");\n\t\tif (event)\n\t\t\tfilter->user_stacktrace_id = event->id;\n\n\t\tfilter->nr_cpus = tep_get_cpus(tep);\n\t\tfilter->last_printed = calloc(filter->nr_cpus, sizeof(*filter->last_printed));\n\t\tif (!filter->last_printed) {\n\t\t\ttracecmd_warning(\"Could not allocate last_printed array for stack trace filtering\");\n\t\t\tfilter->kernel_stacktrace_id = -1;\n\t\t\tfilter->user_stacktrace_id = -1;\n\t\t}\n\t}\n\n\tfor (i = 0; i < filter->nr_filters; i++) {\n\t\tret = tep_filter_match(filter->event_filters[i]->filter, record);\n\t\tswitch (ret) {\n\t\tcase TRACECMD_FILTER_NONE:\n\t\tcase TRACECMD_FILTER_MATCH:\n\t\t\tfound = true;\n\t\t}\n\t\tif (found)\n\t\t\tbreak;\n\t}\n\n\tif (!found && filter->nr_filters) {\n\t\t/* If this is a stack trace and the last event was printed continue */\n\t\tif (!test_stacktraces(filter, record))\n\t\t\treturn TRACECMD_FILTER_MISS;\n\n\t\tis_stack = true;\n\t}\n\n\tfound = false;\n\t/* We need to test all negative filters */\n\tfor (i = 0; i < filter->nr_notrace; i++) {\n\t\tret = tep_filter_match(filter->event_notrace[i]->filter, record);\n\t\tswitch (ret) {\n\t\tcase TRACECMD_FILTER_NONE:\n\t\tcase TRACECMD_FILTER_MATCH:\n\t\t\tfound = true;\n\t\t}\n\t\tif (found)\n\t\t\tbreak;\n\t}\n\n\tif (filter->last_printed)\n\t\tfilter->last_printed[record->cpu] = !is_stack && !found;\n\n\treturn found ? TRACECMD_FILTER_MISS : TRACECMD_FILTER_MATCH;\n}\n\nstruct tracecmd_filter *tracecmd_filter_add(struct tracecmd_input *handle,\n\t\t\t\t\t    const char *filter_str, bool neg)\n{\n\tstruct tracecmd_filter *trace_filter;\n\tstruct tep_handle *tep;\n\tstruct filter ***filter_ptr;\n\tstruct filter **filters;\n\tstruct filter *filter;\n\tint *nr;\n\tint ret;\n\n\tfilter = calloc(1, sizeof(*filter));\n\tif (!filter)\n\t\treturn NULL;\n\n\ttep = tracecmd_get_tep(handle);\n\n\ttrace_filter = tcmd_filter_get(handle);\n\tif (!trace_filter) {\n\t\ttrace_filter = calloc(1, sizeof(*trace_filter));\n\t\tif (!trace_filter)\n\t\t\tgoto fail;\n\t\ttcmd_filter_set(handle, trace_filter);\n\t\ttrace_filter->tep = tep;\n\t}\n\n\tfilter->filter = tep_filter_alloc(tep);\n\tif (!filter->filter)\n\t\tgoto fail;\n\n\tret = tep_filter_add_filter_str(filter->filter, filter_str);\n\tif (ret < 0)\n\t\tgoto fail;\n\n\tif (neg) {\n\t\tfilter_ptr = &trace_filter->event_notrace;\n\t\tnr = &trace_filter->nr_notrace;\n\t} else {\n\t\tfilter_ptr = &trace_filter->event_filters;\n\t\tnr = &trace_filter->nr_filters;\n\t}\n\n\tfilters = realloc(*filter_ptr, sizeof(*filters) * (*nr + 1));\n\tif (!filters)\n\t\tgoto fail;\n\n\t*filter_ptr = filters;\n\tfilters[*nr] = filter;\n\t(*nr)++;\n\treturn trace_filter;\n fail:\n\tif (filter) {\n\t\ttep_filter_free(filter->filter);\n\t\tfree(filter);\n\t}\n\treturn NULL;\n}\n\nstatic void free_filters (struct filter **filter, int nr)\n{\n\tint i;\n\n\tfor (i = 0; i < nr; i++) {\n\t\ttep_filter_free(filter[i]->filter);\n\t\tfree(filter[i]);\n\t}\n\n\tfree(filter);\n}\n\n__hidden void tcmd_filter_free(struct tracecmd_filter *trace_filter)\n{\n\tif (!trace_filter)\n\t\treturn;\n\n\tfree_filters(trace_filter->event_filters, trace_filter->nr_filters);\n\tfree_filters(trace_filter->event_notrace, trace_filter->nr_notrace);\n\n\tfree(trace_filter);\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-ftrace.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/param.h>\n\n#include \"trace-cmd-private.h\"\n\n#define MAX_LINUX_ERRNO   4095\n#define IS_LINUX_ERR_VALUE(x) ((unsigned long long)(void *)(x) >= (unsigned long long)-MAX_LINUX_ERRNO)\n\nstruct tep_plugin_option trace_ftrace_options[] = {\n\t{\n\t\t.name = \"tailprint\",\n\t\t.plugin_alias = \"fgraph\",\n\t\t.description =\n\t\t\"Print function name at function exit in function graph\",\n\t},\n\t{\n\t\t.name = \"depth\",\n\t\t.plugin_alias = \"fgraph\",\n\t\t.description =\n\t\t\"Show the depth of each entry\",\n\t},\n\t{\n\t\t.name = \"retval-skip\",\n\t\t.plugin_alias = \"fgraph\",\n\t\t.description =\n\t\t\"Skip printing function retval in function graph\",\n\t},\n\t{\n\t\t.name = \"retval-dec\",\n\t\t.plugin_alias = \"fgraph\",\n\t\t.description =\n\t\t\"Print function retval in decimal at function exit in function graph\",\n\t},\n\t{\n\t\t.name = \"retval-hex\",\n\t\t.plugin_alias = \"fgraph\",\n\t\t.description =\n\t\t\"Print function retval in hex at function exit in function graph\",\n\t},\n\t{\n\t\t.name = NULL,\n\t}\n};\n\nstatic struct tep_plugin_option *fgraph_tail = &trace_ftrace_options[0];\nstatic struct tep_plugin_option *fgraph_depth = &trace_ftrace_options[1];\nstatic struct tep_plugin_option *fgraph_retval_skip = &trace_ftrace_options[2];\nstatic struct tep_plugin_option *fgraph_retval_dec = &trace_ftrace_options[3];\nstatic struct tep_plugin_option *fgraph_retval_hex = &trace_ftrace_options[4];\n\nstatic int find_ret_event(struct tracecmd_ftrace *finfo, struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\t/* Store the func ret id and event for later use */\n\tevent = tep_find_event_by_name(pevent, \"ftrace\", \"funcgraph_exit\");\n\tif (!event)\n\t\treturn -1;\n\n\tfinfo->fgraph_ret_id = event->id;\n\tfinfo->fgraph_ret_event = event;\n\treturn 0;\n}\n\n#define ret_event_check(finfo, pevent)\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tif (!finfo->fgraph_ret_event && find_ret_event(finfo, pevent) < 0) \\\n\t\t\treturn -1;\t\t\t\t\t\\\n\t} while (0)\n\nstatic int function_handler(struct trace_seq *s, struct tep_record *record,\n\t\t\t    struct tep_event *event, void *context)\n{\n\tstruct tep_handle *pevent = event->tep;\n\tunsigned long long function;\n\tconst char *func;\n\n\tif (tep_get_field_val(s, event, \"ip\", record, &function, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\tfunc = tep_find_function(pevent, function);\n\tif (func)\n\t\ttrace_seq_printf(s, \"%s <-- \", func);\n\telse\n\t\ttrace_seq_printf(s, \"0x%llx\", function);\n\n\tif (tep_get_field_val(s, event, \"parent_ip\", record, &function, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\tfunc = tep_find_function(pevent, function);\n\tif (func)\n\t\ttrace_seq_printf(s, \"%s\", func);\n\telse\n\t\ttrace_seq_printf(s, \"0x%llx\", function);\n\n\treturn 0;\n}\n\n#define TRACE_GRAPH_INDENT\t\t2\n\nstatic struct tep_record *\nget_return_for_leaf(struct trace_seq *s, int cpu, int cur_pid,\n\t\t    unsigned long long cur_func, struct tep_record *next,\n\t\t    struct tracecmd_ftrace *finfo)\n{\n\tunsigned long long val;\n\tunsigned long long type;\n\tunsigned long long pid;\n\n\t/* Searching a common field, can use any event */\n\tif (tep_get_common_field_val(s, finfo->fgraph_ret_event, \"common_type\", next, &type, 1))\n\t\treturn NULL;\n\n\tif (type != finfo->fgraph_ret_id)\n\t\treturn NULL;\n\n\tif (tep_get_common_field_val(s, finfo->fgraph_ret_event, \"common_pid\", next, &pid, 1))\n\t\treturn NULL;\n\n\tif (cur_pid != pid)\n\t\treturn NULL;\n\n\t/* We aleady know this is a funcgraph_ret_event */\n\tif (tep_get_field_val(s, finfo->fgraph_ret_event, \"func\", next, &val, 1))\n\t\treturn NULL;\n\n\tif (cur_func != val)\n\t\treturn NULL;\n\n\t/* this is a leaf, now advance the iterator */\n\treturn tracecmd_read_data(tracecmd_curr_thread_handle, cpu);\n}\n\n/* Signal a overhead of time execution to the output */\nstatic void print_graph_overhead(struct trace_seq *s,\n\t\t\t\t unsigned long long duration)\n{\n\t/* Non nested entry or return */\n\tif (duration == ~0ULL)\n\t\treturn (void)trace_seq_printf(s, \"  \");\n\n\t/* Duration exceeded 1 sec */\n\tif (duration > 1000000000ULL)\n\t\treturn (void)trace_seq_printf(s, \"$ \");\n\n\t/* Duration exceeded 1000 usecs */\n\tif (duration > 1000000ULL)\n\t\treturn (void)trace_seq_printf(s, \"# \");\n\n\t/* Duration exceeded 100 usecs */\n\tif (duration > 100000ULL)\n\t\treturn (void)trace_seq_printf(s, \"! \");\n\n\t/* Duration exceeded 10 usecs */\n\tif (duration > 10000ULL)\n\t\treturn (void)trace_seq_printf(s, \"+ \");\n\n\ttrace_seq_printf(s, \"  \");\n}\n\nstatic void print_graph_duration(struct trace_seq *s, unsigned long long duration)\n{\n\tunsigned long usecs = duration / 1000;\n\tunsigned long nsecs_rem = duration % 1000;\n\t/* log10(ULONG_MAX) + '\\0' */\n\tchar msecs_str[21];\n\tchar nsecs_str[5];\n\tint len;\n\tint i;\n\n\tsprintf(msecs_str, \"%lu\", usecs);\n\n\t/* Print msecs */\n\tlen = s->len;\n\ttrace_seq_printf(s, \"%lu\", usecs);\n\n\t/* Print nsecs (we don't want to exceed 7 numbers) */\n\tif ((s->len - len) < 7) {\n\t\tsnprintf(nsecs_str, MIN(sizeof(nsecs_str), 8 - len), \"%03lu\", nsecs_rem);\n\t\ttrace_seq_printf(s, \".%s\", nsecs_str);\n\t}\n\n\tlen = s->len - len;\n\n\ttrace_seq_puts(s, \" us \");\n\n\t/* Print remaining spaces to fit the row's width */\n\tfor (i = len; i < 7; i++)\n\t\ttrace_seq_putc(s, ' ');\n\n\ttrace_seq_puts(s, \"|  \");\n}\n\n/* Returns true if it printed args, otherwise it returns false */\nstatic bool print_args(struct trace_seq *s, struct tep_event *event,\n\t\t       struct tep_record *record, const char *func)\n{\n\tstruct tep_format_field *field;\n\tint long_size = tep_get_long_size(event->tep);\n\tvoid *args;\n\tint len;\n\n\tif (!long_size)\n\t\tlong_size = sizeof(long);\n\n\tfield = tep_find_field(event, \"args\");\n\tif (!field)\n\t\treturn false;\n\n\tlen = record->size - field->offset;\n\n\tlen /= long_size;\n\n\targs = record->data + field->offset;\n\n\ttep_btf_print_args(event->tep, s, args, len, long_size, func);\n\n\treturn true;\n}\n\nstatic int\nprint_graph_entry_leaf(struct trace_seq *s,\n\t\t       struct tep_event *event,\n\t\t       struct tep_record *record,\n\t\t       struct tep_record *ret_rec,\n\t\t       struct tracecmd_ftrace *finfo)\n{\n\tstruct tep_handle *pevent = event->tep;\n\tunsigned long long rettime, calltime;\n\tunsigned long long duration, depth;\n\tunsigned long long val;\n\tunsigned long long retval;\n\tbool fgraph_retval_supported = true;\n\tconst char *retfunc = NULL;\n\tconst char *func;\n\tint ret;\n\tint i;\n\n\tif (tep_get_field_val(s, finfo->fgraph_ret_event, \"rettime\", ret_rec, &rettime, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\tif (tep_get_field_val(s, finfo->fgraph_ret_event, \"calltime\", ret_rec, &calltime, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\tif (!tep_find_field(finfo->fgraph_ret_event, \"retval\")) {\n\t\tfgraph_retval_supported = false;\n\t} else {\n\t\tif (tep_get_field_val(s, finfo->fgraph_ret_event, \"retval\", ret_rec, &retval, 1))\n\t\t\treturn trace_seq_putc(s, '!');\n\t}\n\n\t/* In case this is a retaddr event */\n\tif (!tep_get_field_val(s, event, \"retaddr\", record, &val, 0))\n\t\tretfunc = tep_find_function(pevent, val);\n\n\tduration = rettime - calltime;\n\n\t/* Overhead */\n\tprint_graph_overhead(s, duration);\n\n\t/* Duration */\n\tprint_graph_duration(s, duration);\n\n\tif (tep_get_field_val(s, event, \"depth\", record, &depth, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\t/* Function */\n\tfor (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)\n\t\ttrace_seq_putc(s, ' ');\n\n\tif (tep_get_field_val(s, event, \"func\", record, &val, 1))\n\t\treturn trace_seq_putc(s, '!');\n\tfunc = tep_find_function(pevent, val);\n\n\tif (func) {\n\t\tret = trace_seq_printf(s, \"%s(\", func);\n\t\tprint_args(s, event, record, func);\n\t\tret = trace_seq_puts(s, \");\");\n\t} else\n\t\tret = trace_seq_printf(s, \"%llx();\", val);\n\n\tif (ret && fgraph_depth->set)\n\t\tret = trace_seq_printf(s, \" (%lld)\", depth);\n\n\tif (retfunc)\n\t\tret = trace_seq_printf(s, \" /* <-%s */\", retfunc);\n\n\t/* Return Value */\n\tif (ret && fgraph_retval_supported && !fgraph_retval_skip->set) {\n\t\tif (fgraph_retval_dec->set) {\n\t\t\tret = trace_seq_printf(s, \" (ret=%lld)\", retval);\n\t\t} else if (fgraph_retval_hex->set) {\n\t\t\tret = trace_seq_printf(s, \" (ret=0x%llx)\", retval);\n\t\t} else {\n\t\t\t/* Error codes are in decimal; others are in hex */\n\t\t\tif (!IS_LINUX_ERR_VALUE(retval))\n\t\t\t\tret = trace_seq_printf(s, \" (ret=0x%llx)\", retval);\n\t\t\telse\n\t\t\t\tret = trace_seq_printf(s, \" (ret=%lld)\", retval);\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nstatic int print_graph_nested(struct trace_seq *s,\n\t\t\t      struct tep_event *event,\n\t\t\t      struct tep_record *record)\n{\n\tstruct tep_handle *pevent = event->tep;\n\tunsigned long long depth;\n\tunsigned long long val;\n\tconst char *retfunc = NULL;\n\tconst char *func;\n\tint ret;\n\tint i;\n\n\t/* No overhead */\n\tprint_graph_overhead(s, -1);\n\n\t/* No time */\n\ttrace_seq_puts(s, \"           |  \");\n\n\tif (tep_get_field_val(s, event, \"depth\", record, &depth, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\t/* In case this is a retaddr event */\n\tif (!tep_get_field_val(s, event, \"retaddr\", record, &val, 0))\n\t\tretfunc = tep_find_function(pevent, val);\n\n\t/* Function */\n\tfor (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)\n\t\ttrace_seq_putc(s, ' ');\n\n\tif (tep_get_field_val(s, event, \"func\", record, &val, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\tfunc = tep_find_function(pevent, val);\n\n\n\tif (func) {\n\t\tret = trace_seq_printf(s, \"%s(\", func);\n\t\tprint_args(s, event, record, func);\n\t\tif (retfunc)\n\t\t\tret = trace_seq_printf(s, \") /* <-%s */ {\", retfunc);\n\t\telse\n\t\t\tret = trace_seq_puts(s, \") {\");\n\t} else {\n\t\tif (retfunc)\n\t\t\tret = trace_seq_printf(s, \"%llx() /* <-%s */ {\", val, retfunc);\n\t\telse\n\t\t\tret = trace_seq_puts(s, \") {\");\n\t}\n\n\tif (ret && fgraph_depth->set)\n\t\tret = trace_seq_printf(s, \" (%lld)\", depth);\n\n\treturn ret;\n}\n\nstatic int\nfgraph_ent_handler(struct trace_seq *s, struct tep_record *record,\n\t\t   struct tep_event *event, void *context)\n{\n\tstruct tracecmd_ftrace *finfo = context;\n\tstruct tep_record *rec;\n\tunsigned long long val, pid;\n\tint cpu;\n\n\tret_event_check(finfo, event->tep);\n\n\tif (tep_get_common_field_val(s, event, \"common_pid\", record, &pid, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\tif (tep_get_field_val(s, event, \"func\", record, &val, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\trec = tracecmd_peek_next_data(tracecmd_curr_thread_handle, &cpu);\n\n\t/*\n\t * If the next event is on another CPU, show it.\n\t * Even if the next event is the return of this function.\n\t */\n\tif (cpu != record->cpu)\n\t\trec = NULL;\n\n\tif (rec)\n\t\trec = get_return_for_leaf(s, cpu, pid, val, rec, finfo);\n\n\tif (rec) {\n\t\t/*\n\t\t * If this is a leaf function, then get_return_for_leaf\n\t\t * returns the return of the function\n\t\t */\n\t\tprint_graph_entry_leaf(s, event, record, rec, finfo);\n\t\ttracecmd_free_record(rec);\n\t} else\n\t\tprint_graph_nested(s, event, record);\n\n\treturn 0;\n}\n\nstatic int\nfgraph_ret_handler(struct trace_seq *s, struct tep_record *record,\n\t\t   struct tep_event *event, void *context)\n{\n\tstruct tracecmd_ftrace *finfo = context;\n\tunsigned long long rettime, calltime;\n\tunsigned long long duration, depth;\n\tunsigned long long val;\n\tconst char *func;\n\tunsigned long long retval;\n\tbool fgraph_retval_supported = true;\n\tint i;\n\n\tret_event_check(finfo, event->tep);\n\n\tif (tep_get_field_val(s, event, \"rettime\", record, &rettime, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\tif (tep_get_field_val(s, event, \"calltime\", record, &calltime, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\tif (!tep_find_field(event, \"retval\")) {\n\t\tfgraph_retval_supported = false;\n\t} else {\n\t\tif (tep_get_field_val(s, event, \"retval\", record, &retval, 1))\n\t\t\treturn trace_seq_putc(s, '!');\n\t}\n\n\tduration = rettime - calltime;\n\n\t/* Overhead */\n\tprint_graph_overhead(s, duration);\n\n\t/* Duration */\n\tprint_graph_duration(s, duration);\n\n\tif (tep_get_field_val(s, event, \"depth\", record, &depth, 1))\n\t\treturn trace_seq_putc(s, '!');\n\n\t/* Function */\n\tfor (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)\n\t\ttrace_seq_putc(s, ' ');\n\n\ttrace_seq_putc(s, '}');\n\n\tif (fgraph_tail->set) {\n\t\tif (tep_get_field_val(s, event, \"func\", record, &val, 0))\n\t\t\treturn 0;\n\t\tfunc = tep_find_function(event->tep, val);\n\t\tif (!func)\n\t\t\treturn 0;\n\t\ttrace_seq_printf(s, \" /* %s */\", func);\n\t}\n\n\tif (fgraph_depth->set)\n\t\ttrace_seq_printf(s, \" (%lld)\", depth);\n\n\t/* Return Value */\n\tif (fgraph_retval_supported && !fgraph_retval_skip->set) {\n\t\tif (fgraph_retval_dec->set) {\n\t\t\ttrace_seq_printf(s, \" (ret=%lld)\", retval);\n\t\t} else if (fgraph_retval_hex->set) {\n\t\t\ttrace_seq_printf(s, \" (ret=0x%llx)\", retval);\n\t\t} else {\n\t\t\t/* Error codes are in decimal; others are in hex */\n\t\t\tif (!IS_LINUX_ERR_VALUE(retval))\n\t\t\t\ttrace_seq_printf(s, \" (ret=0x%llx)\", retval);\n\t\t\telse\n\t\t\t\ttrace_seq_printf(s, \" (ret=%lld)\", retval);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n/**\n * tracecmd_ftrace_load_options - load the ftrace options\n *\n * This routine is used for trace-cmd list, to load the builtin\n * ftrace options in order to list them. As the list command does\n * not load a trace.dat file where this would normally be loaded.\n */\nvoid tracecmd_ftrace_load_options(void)\n{\n\ttep_plugin_add_options(\"ftrace\", trace_ftrace_options);\n}\n\nint tracecmd_ftrace_overrides(struct tracecmd_input *handle,\n\tstruct tracecmd_ftrace *finfo)\n{\n\tstruct tep_handle *pevent;\n\tstruct tep_event *event;\n\n\tfinfo->handle = handle;\n\n\tpevent = tracecmd_get_tep(handle);\n\n\ttep_register_event_handler(pevent, -1, \"ftrace\", \"function\",\n\t\t\t\t      function_handler, NULL);\n\n\ttep_register_event_handler(pevent, -1, \"ftrace\", \"funcgraph_entry\",\n\t\t\t\t      fgraph_ent_handler, finfo);\n\n\ttep_register_event_handler(pevent, -1, \"ftrace\", \"fgraph_retaddr_entry\",\n\t\t\t\t      fgraph_ent_handler, finfo);\n\n\ttep_register_event_handler(pevent, -1, \"ftrace\", \"funcgraph_exit\",\n\t\t\t\t      fgraph_ret_handler, finfo);\n\n\ttep_plugin_add_options(\"ftrace\", trace_ftrace_options);\n\n\t/* Store the func ret id and event for later use */\n\tevent = tep_find_event_by_name(pevent, \"ftrace\", \"funcgraph_exit\");\n\tif (!event)\n\t\treturn 0;\n\n\tfinfo->long_size = tracecmd_long_size(handle);\n\n\tfinfo->fgraph_ret_id = event->id;\n\tfinfo->fgraph_ret_event = event;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-hash.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2014, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <errno.h>\n\n#include \"trace-cmd-private.h\"\n#include \"trace-hash.h\"\n\nint __hidden tcmd_hash_init(struct trace_hash *hash, int buckets)\n{\n\tmemset(hash, 0, sizeof(*hash));\n\n\thash->buckets = calloc(sizeof(*hash->buckets), buckets);\n\tif (!hash->buckets)\n\t\treturn -ENOMEM;\n\thash->nr_buckets = buckets;\n\n\t/* If a power of two then we can shortcut */\n\tif (!(buckets & (buckets - 1)))\n\t\thash->power = buckets - 1;\n\n\treturn 0;\n}\n\nvoid __hidden tcmd_hash_free(struct trace_hash *hash)\n{\n\tfree(hash->buckets);\n}\n\nint __hidden tcmd_hash_empty(struct trace_hash *hash)\n{\n\tstruct trace_hash_item **bucket;\n\n\ttrace_hash_for_each_bucket(bucket, hash)\n\t\tif (*bucket)\n\t\t\treturn 0;\n\treturn 1;\n}\n\nint __hidden tcmd_hash_add(struct trace_hash *hash, struct trace_hash_item *item)\n{\n\tstruct trace_hash_item *next;\n\tint bucket = hash->power ? item->key & hash->power :\n\t\titem->key % hash->nr_buckets;\n\n\tif (hash->buckets[bucket]) {\n\t\tnext = hash->buckets[bucket];\n\t\tnext->prev = item;\n\t} else\n\t\tnext = NULL;\n\n\titem->next = next;\n\titem->prev = (struct trace_hash_item *)&hash->buckets[bucket];\n\n\thash->buckets[bucket] = item;\n\n\treturn 1;\n}\n\n __hidden struct trace_hash_item *\ntcmd_hash_find(struct trace_hash *hash, unsigned long long key,\n\t       trace_hash_func match, void *data)\n{\n\tstruct trace_hash_item *item;\n\tint bucket = hash->power ? key & hash->power :\n\t\tkey % hash->nr_buckets;\n\n\tfor (item = hash->buckets[bucket]; item; item = item->next) {\n\t\tif (item->key == key) {\n\t\t\tif (!match)\n\t\t\t\treturn item;\n\t\t\tif (match(item, data))\n\t\t\t\treturn item;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-hooks.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2015 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n\n#include \"trace-cmd-private.h\"\n#include \"trace-cmd-local.h\"\n#include \"event-utils.h\"\n\nstruct hook_list *tracecmd_create_event_hook(const char *arg)\n{\n\tstruct hook_list *hook;\n\tchar *system = NULL;\n\tchar *event;\n\tchar *match;\n\tchar *flags = NULL;\n\tchar *pid = NULL;\n\tchar *str;\n\tchar *tok;\n\tint index;\n\tint ch;\n\tint i;\n\n\thook = malloc(sizeof(*hook));\n\tif (!hook)\n\t\treturn NULL;\n\tmemset(hook, 0, sizeof(*hook));\n\n\tstr = strdup(arg);\n\tif (!str) {\n\t\tfree(hook);\n\t\treturn NULL;\n\t}\n\n\thook->str = str;\n\thook->hook = arg;\n\n\t/*\n\t * Hooks are in the form of:\n\t *  [<start_system>:]<start_event>,<start_match>[,<start_pid>]/\n\t *  [<end_system>:]<end_event>,<end_match>[,<flags>]\n\t *\n\t * Where start_system, start_pid, end_system, and flags are all\n\t * optional.\n\t *\n\t * Flags are (case insensitive):\n\t *  P - pinned to cpu (wont migrate)\n\t *  G - global, not hooked to task - currently ignored.\n\t *  S - save stacks for this event.\n\t */\n\ttok = strtok(str, \":,\");\n\tif (!tok)\n\t\tgoto invalid_tok;\n\n\t/* See what the token was from the original arg */\n\tindex = strlen(tok);\n\tif (arg[index] == ':') {\n\t\t/* this is a system, the next token must be ',' */\n\t\tsystem = tok;\n\t\ttok = strtok(NULL, \",\");\n\t\tif (!tok)\n\t\t\tgoto invalid_tok;\n\t}\n\tevent = tok;\n\n\ttok = strtok(NULL, \",/\");\n\tif (!tok)\n\t\tgoto invalid_tok;\n\tmatch = tok;\n\tindex = strlen(tok) + tok - str;\n\tif (arg[index] == ',') {\n\t\ttok = strtok(NULL, \"/\");\n\t\tif (!tok)\n\t\t\tgoto invalid_tok;\n\t\tpid = tok;\n\t}\n\n\thook->start_system = system;\n\thook->start_event = event;\n\thook->start_match = match;\n\thook->pid = pid;\n\n\t/* Now process the end event */\n\tsystem = NULL;\n\n\ttok = strtok(NULL, \":,\");\n\tif (!tok)\n\t\tgoto invalid_tok;\n\n\t/* See what the token was from the original arg */\n\tindex = tok - str + strlen(tok);\n\tif (arg[index] == ':') {\n\t\t/* this is a system, the next token must be ',' */\n\t\tsystem = tok;\n\t\ttok = strtok(NULL, \",\");\n\t\tif (!tok)\n\t\t\tgoto invalid_tok;\n\t}\n\tevent = tok;\n\n\ttok = strtok(NULL, \",\");\n\tif (!tok)\n\t\tgoto invalid_tok;\n\tmatch = tok;\n\tindex = strlen(tok) + tok - str;\n\tif (arg[index] == ',') {\n\t\ttok = strtok(NULL, \"\");\n\t\tif (!tok)\n\t\t\tgoto invalid_tok;\n\t\tflags = tok;\n\t}\n\n\thook->end_system = system;\n\thook->end_event = event;\n\thook->end_match = match;\n\thook->migrate = 1;\n\tif (flags) {\n\t\tfor (i = 0; flags[i]; i++) {\n\t\t\tch = tolower(flags[i]);\n\t\t\tswitch (ch) {\n\t\t\tcase 'p':\n\t\t\t\thook->migrate = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'g':\n\t\t\t\thook->global = 1;\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\thook->stack = 1;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttracecmd_warning(\"unknown flag %c\", flags[i]);\n\t\t\t}\n\t\t}\n\t}\n\n\tprintf(\"start %s:%s:%s (%s) end %s:%s:%s (%s)\\n\",\n\t       hook->start_system,\n\t       hook->start_event,\n\t       hook->start_match,\n\t       hook->pid,\n\t       hook->end_system,\n\t       hook->end_event,\n\t       hook->end_match,\n\t       flags);\n\treturn hook;\n\ninvalid_tok:\n\ttracecmd_warning(\"Invalid hook format '%s'\", arg);\n\tfree(hook->str);\n\tfree(hook);\n\treturn NULL;\n}\n\nvoid tracecmd_free_hooks(struct hook_list *hooks)\n{\n\tstruct hook_list *hook;\n\n\twhile (hooks) {\n\t\thook = hooks;\n\t\thooks = hooks->next;\n\n\t\tfree(hook->str);\n\t\tfree(hook);\n\t}\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-input.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <regex.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <errno.h>\n\n#include <linux/time64.h>\n\n#include \"trace-write-local.h\"\n#include \"trace-cmd-local.h\"\n#include \"trace-rbtree.h\"\n#include \"trace-local.h\"\n#include \"kbuffer.h\"\n#include \"list.h\"\n\n#define _STRINGIFY(x) #x\n#define STRINGIFY(x) _STRINGIFY(x)\n\n#define MISSING_EVENTS (1 << 31)\n#define MISSING_STORED (1 << 30)\n\n#define COMMIT_MASK ((1 << 27) - 1)\n\n/* force uncompressing in memory */\n#define INMEMORY_DECOMPRESS\n\n/* for debugging read instead of mmap */\nstatic int force_read = 0;\n\nstruct page_map {\n\tstruct list_head\tlist;\n\toff_t\t\t\toffset;\n\toff_t\t\t\tsize;\n\tvoid\t\t\t*map;\n\tint\t\t\tref_count;\n};\n\nstruct follow_event {\n\tstruct tep_event\t*event;\n\tvoid\t\t\t*callback_data;\n\tint (*callback)(struct tracecmd_input *handle,\n\t\t\tstruct tep_event *,\n\t\t\tstruct tep_record *,\n\t\t\tint, void *);\n};\n\nstruct page {\n\tstruct list_head\tlist;\n\toff_t\t\t\toffset;\n\tstruct tracecmd_input\t*handle;\n\tstruct page_map\t\t*page_map;\n\tvoid\t\t\t*map;\n\tint\t\t\tref_count;\n\tint\t\t\tcpu;\n\tlong long\t\tlost_events;\n#if DEBUG_RECORD\n\tstruct tep_record\t*records;\n#endif\n};\n\nstruct zchunk_cache {\n\tstruct trace_rbtree_node\tnode;\n\tstruct tracecmd_compress_chunk *chunk;\n\tvoid\t\t\t\t*map;\n\tint\t\t\t\tref;\n};\n\nstruct cpu_zdata {\n\t/* uncompressed cpu data */\n\tint\t\t\tfd;\n\tchar\t\t\tfile[26]; /* strlen(COMPR_TEMP_FILE) */\n\tunsigned int\t\tcount;\n\tunsigned int\t\tlast_chunk;\n\tstruct trace_rbtree\tcache;\n\tstruct tracecmd_compress_chunk\t*chunks;\n};\n\n#define COMPR_TEMP_FILE \"/tmp/trace_cpu_dataXXXXXX\"\nstruct cpu_data {\n\t/* the first two never change */\n\tunsigned long long\tfile_offset;\n\tunsigned long long\tfile_size;\n\tunsigned long long\toffset;\n\tunsigned long long\tsize;\n\tunsigned long long\ttimestamp;\n\tunsigned long long\tfirst_ts;\n\tstruct list_head\tpage_maps;\n\tstruct page_map\t\t*page_map;\n\tstruct page\t\t**pages;\n\tstruct tep_record\t*next;\n\tstruct page\t\t*page;\n\tstruct kbuffer\t\t*kbuf;\n\tint\t\t\tnr_pages;\n\tint\t\t\tpage_cnt;\n\tint\t\t\tcpu;\n\tint\t\t\tpipe_fd;\n\tstruct cpu_zdata\tcompress;\n};\n\nstruct cpu_file_data {\n\tint\t\t\tcpu;\n\tunsigned long long\toffset;\n\tunsigned long long\tsize;\n};\n\nstruct input_buffer_instance {\n\tchar\t\t\t*name;\n\tsize_t\t\t\toffset;\n\tchar\t\t\t*clock;\n\tbool\t\t\tlatency;\n\tint\t\t\tpage_size;\n\tint\t\t\tcpus;\n\tstruct cpu_file_data\t*cpu_data;\n};\n\nstruct ts_offset_sample {\n\tlong long\ttime;\n\tlong long\toffset;\n\tlong long\tscaling;\n\tlong long\tfraction;\n};\n\nstruct guest_trace_info {\n\tstruct guest_trace_info\t*next;\n\tchar\t\t\t*name;\n\tunsigned long long\ttrace_id;\n\tint\t\t\tvcpu_count;\n\tint\t\t\t*cpu_pid;\n};\n\nstruct timesync_offsets {\n\tint\tts_samples_count;\n\tstruct ts_offset_sample\t*ts_samples;\n};\n\nstruct host_trace_info {\n\tunsigned long long\tpeer_trace_id;\n\tunsigned int\t\tflags;\n\tbool\t\t\tsync_enable;\n\tint\t\t\tts_samples_count;\n\tstruct ts_offset_sample\t*ts_samples;\n\tint\t\t\tcpu_count;\n\tstruct timesync_offsets\t*ts_offsets;\n};\n\nstruct tsc2nsec {\n\tint\tmult;\n\tint\tshift;\n\tunsigned long long offset;\n};\n\nstruct file_section {\n\tunsigned long long\t\tsection_offset;\n\tunsigned long long\t\tdata_offset;\n\tint\t\t\t\tid;\n\tint\t\t\t\tflags;\n\tstruct file_section\t\t*next;\n};\n\nstruct tracecmd_input {\n\tstruct tep_handle\t*pevent;\n\tstruct tep_plugin_list\t*plugin_list;\n\tstruct tracecmd_input\t*parent;\n\tstruct tracecmd_filter\t*filter;\n\tstruct follow_event\t*followers;\n\tstruct follow_event\t*missed_followers;\n\tstruct tracecmd_cpu_map *map;\n\tunsigned long\t\tfile_state;\n\tunsigned long long\ttrace_id;\n\tunsigned long long\tnext_offset;\n\tunsigned long\t\tflags;\n\tint\t\t\tfd;\n\tint\t\t\tlong_size;\n\tint\t\t\tpage_size;\n\tint\t\t\tpage_map_size;\n\tint\t\t\tmax_cpu;\n\tint\t\t\tcpus;\n\tint\t\t\tstart_cpu;\n\tint\t\t\tref;\n\tint\t\t\tnr_followers;\n\tint\t\t\tnr_missed_followers;\n\tint\t\t\tnr_buffers;\t/* buffer instances */\n\tbool\t\t\tuse_trace_clock;\n\tbool\t\t\tread_page;\n\tbool\t\t\tuse_pipe;\n\tbool\t\t\tread_zpage; /* uncompress pages in memory, do not use tmp files */\n\tbool\t\t\tcpu_compressed;\n\tint\t\t\tfile_version;\n\tint\t\t\tmap_cnt;\n\tunsigned int\t\tcpustats_size;\n\tstruct cpu_zdata\tlatz;\n\tstruct cpu_data \t*cpu_data;\n\tlong long\t\tts_offset;\n\tstruct tsc2nsec\t\ttsc_calc;\n\n\tunsigned int\t\tstrings_size;\t/* size of the metadata strings */\n\tchar\t\t\t*strings;\t/* metadata strings */\n\n\tbool\t\t\tread_compress;\n\tstruct tracecmd_compression *compress;\n\n\tstruct host_trace_info\thost;\n\tdouble\t\t\tts2secs;\n\tchar *\t\t\tcpustats;\n\tchar *\t\t\tuname;\n\tchar *\t\t\tversion;\n\tchar *\t\t\ttrace_clock;\n\tstruct input_buffer_instance\ttop_buffer;\n\tstruct input_buffer_instance\t*buffers;\n\tint\t\t\tparsing_failures;\n\tstruct guest_trace_info\t*guest;\n\n\tstruct tracecmd_ftrace\tfinfo;\n\n\tstruct hook_list\t*hooks;\n\tstruct pid_addr_maps\t*pid_maps;\n\t/* file information */\n\tstruct file_section\t*sections;\n\tbool\t\t\toptions_init;\n\tunsigned long long\toptions_start;\n\tunsigned long long\toptions_last_offset;\n\tsize_t\t\t\ttotal_file_size;\n\n\t/* For custom profilers. */\n\ttracecmd_show_data_func\tshow_data_func;\n\n\tvoid\t\t\t*private;\n};\n\n__thread struct tracecmd_input *tracecmd_curr_thread_handle;\n\n#define CHECK_READ_STATE(H, S) ((H)->file_version < FILE_VERSION_SECTIONS && (H)->file_state >= (S))\n#define HAS_SECTIONS(H) ((H)->flags & TRACECMD_FL_SECTIONED)\n#define HAS_COMPRESSION(H) ((H)->flags & TRACECMD_FL_COMPRESSION)\n\nstatic int read_options_type(struct tracecmd_input *handle);\n\nvoid tracecmd_set_flag(struct tracecmd_input *handle, int flag)\n{\n\thandle->flags |= flag;\n}\n\nvoid tracecmd_clear_flag(struct tracecmd_input *handle, int flag)\n{\n\thandle->flags &= ~flag;\n}\n\nunsigned long tracecmd_get_flags(struct tracecmd_input *handle)\n{\n\treturn handle->flags;\n}\n\nenum tracecmd_file_states tracecmd_get_file_state(struct tracecmd_input *handle)\n{\n\treturn handle->file_state;\n}\n\nvoid tracecmd_set_private(struct tracecmd_input *handle, void *data)\n{\n\thandle->private = data;\n}\n\nvoid *tracecmd_get_private(struct tracecmd_input *handle)\n{\n\treturn handle->private;\n}\n\n#if DEBUG_RECORD\nstatic void remove_record(struct page *page, struct tep_record *record)\n{\n\tif (record->prev)\n\t\trecord->prev->next = record->next;\n\telse\n\t\tpage->records = record->next;\n\tif (record->next)\n\t\trecord->next->prev = record->prev;\n}\nstatic void add_record(struct page *page, struct tep_record *record)\n{\n\tif (page->records)\n\t\tpage->records->prev = record;\n\trecord->next = page->records;\n\trecord->prev = NULL;\n\tpage->records = record;\n}\nstatic const char *show_records(struct page **pages, int nr_pages)\n{\n\tstatic char buf[BUFSIZ + 1];\n\tstruct tep_record *record;\n\tstruct page *page;\n\tint len;\n\tint i;\n\n\tmemset(buf, 0, sizeof(buf));\n\tlen = 0;\n\tfor (i = 0; i < nr_pages; i++) {\n\t\tpage = pages[i];\n\t\tif (!page)\n\t\t\tcontinue;\n\t\tfor (record = page->records; record; record = record->next) {\n\t\t\tint n;\n\t\t\tn = snprintf(buf+len, BUFSIZ - len, \" 0x%lx\", record->alloc_addr);\n\t\t\tlen += n;\n\t\t\tif (len >= BUFSIZ)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn buf;\n}\n#else\nstatic inline void remove_record(struct page *page, struct tep_record *record) {}\nstatic inline void add_record(struct page *page, struct tep_record *record) {}\nstatic const char *show_records(struct page **pages, int nr_pages)\n{\n\treturn \"\";\n}\n#endif\n\n/**\n * tcmd_set_guest_map - set map to input handle\n * @handle: The handle to set the cpu map to\n * @map: The cpu map for this handle (to the host)\n *\n * Assign the mapping of host to guest for a guest handle.\n */\n__hidden void tcmd_set_guest_map(struct tracecmd_input *handle,\n\t\t\t\t struct tracecmd_cpu_map *map)\n{\n\thandle->map = map;\n}\n\n__hidden struct tracecmd_cpu_map *tcmd_get_guest_map(struct tracecmd_input *handle)\n{\n\treturn handle->map;\n}\n\n__hidden void tcmd_set_guest_map_cnt(struct tracecmd_input *handle, int count)\n{\n\thandle->map_cnt = count;\n}\n\n__hidden int tcmd_get_guest_map_cnt(struct tracecmd_input *handle)\n{\n\treturn handle->map_cnt;\n}\n\nstatic int init_cpu(struct tracecmd_input *handle, int cpu);\n\nstatic ssize_t do_read_fd(int fd, void *data, size_t size)\n{\n\tssize_t tot = 0;\n\tssize_t r;\n\n\tdo {\n\t\tr = read(fd, data + tot, size - tot);\n\t\ttot += r;\n\n\t\tif (!r)\n\t\t\tbreak;\n\t\tif (r < 0)\n\t\t\treturn r;\n\t} while (tot != size);\n\n\treturn tot;\n}\n\nstatic inline int do_lseek(struct tracecmd_input *handle, int offset, int whence)\n{\n\tif (handle->read_compress)\n\t\treturn tracecmd_compress_lseek(handle->compress, offset, whence);\n\telse\n\t\treturn lseek(handle->fd, offset, whence);\n}\n\nstatic inline ssize_t do_read(struct tracecmd_input *handle, void *data, size_t size)\n{\n\tif (handle->read_compress)\n\t\treturn tracecmd_compress_buffer_read(handle->compress, data, size);\n\telse\n\t\treturn do_read_fd(handle->fd, data, size);\n}\n\nstatic ssize_t\ndo_read_check(struct tracecmd_input *handle, void *data, size_t size)\n{\n\tssize_t ret;\n\n\tret = do_read(handle, data, size);\n\tif (ret < 0)\n\t\treturn ret;\n\tif (ret != size)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic char *read_string(struct tracecmd_input *handle)\n{\n\tchar buf[BUFSIZ];\n\tchar *str = NULL;\n\tsize_t size = 0;\n\tssize_t i;\n\tssize_t r;\n\n\tfor (;;) {\n\t\tr = do_read(handle, buf, BUFSIZ);\n\t\tif (r <= 0)\n\t\t\tgoto fail;\n\n\t\tfor (i = 0; i < r; i++) {\n\t\t\tif (!buf[i])\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i < r)\n\t\t\tbreak;\n\n\t\tif (str) {\n\t\t\tsize += BUFSIZ;\n\t\t\tstr = realloc(str, size);\n\t\t\tif (!str)\n\t\t\t\treturn NULL;\n\t\t\tmemcpy(str + (size - BUFSIZ), buf, BUFSIZ);\n\t\t} else {\n\t\t\tsize = BUFSIZ;\n\t\t\tstr = malloc(size);\n\t\t\tif (!str)\n\t\t\t\treturn NULL;\n\t\t\tmemcpy(str, buf, size);\n\t\t}\n\t}\n\n\t/* move the file descriptor to the end of the string */\n\tr = do_lseek(handle, -(r - (i+1)), SEEK_CUR);\n\tif (r < 0)\n\t\tgoto fail;\n\n\tif (str) {\n\t\tsize += i + 1;\n\t\tstr = realloc(str, size);\n\t\tif (!str)\n\t\t\treturn NULL;\n\t\tmemcpy(str + (size - i), buf, i + 1);\n\t} else {\n\t\tsize = i + 1;\n\t\tstr = malloc(size);\n\t\tif (!str)\n\t\t\treturn NULL;\n\t\tmemcpy(str, buf, i + 1);\n\t}\n\n\treturn str;\n\n fail:\n\tif (str)\n\t\tfree(str);\n\treturn NULL;\n}\n\nstatic int read2(struct tracecmd_input *handle, unsigned short *size)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tunsigned short data;\n\n\tif (do_read_check(handle, &data, 2))\n\t\treturn -1;\n\n\t*size = tep_read_number(pevent, &data, 2);\n\treturn 0;\n}\n\nstatic int read4(struct tracecmd_input *handle, unsigned int *size)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tunsigned int data;\n\n\tif (do_read_check(handle, &data, 4))\n\t\treturn -1;\n\n\t*size = tep_read_number(pevent, &data, 4);\n\treturn 0;\n}\n\nstatic int read8(struct tracecmd_input *handle, unsigned long long *size)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tunsigned long long data;\n\n\tif (do_read_check(handle, &data, 8))\n\t\treturn -1;\n\n\t*size = tep_read_number(pevent, &data, 8);\n\treturn 0;\n}\n\n__hidden void tcmd_in_uncompress_reset(struct tracecmd_input *handle)\n{\n\tif (handle->compress) {\n\t\thandle->read_compress = false;\n\t\ttracecmd_compress_reset(handle->compress);\n\t}\n}\n\n__hidden int tcmd_in_uncompress_block(struct tracecmd_input *handle)\n{\n\tint ret = 0;\n\n\tif (handle->compress) {\n\t\tret = tracecmd_uncompress_block(handle->compress);\n\t\tif (!ret)\n\t\t\thandle->read_compress = true;\n\t}\n\treturn ret;\n}\n\nstatic struct file_section *section_get(struct tracecmd_input *handle, int id)\n{\n\tstruct file_section *sec;\n\n\tfor (sec = handle->sections; sec; sec = sec->next) {\n\t\tif (sec->id == id)\n\t\t\treturn sec;\n\t}\n\n\treturn NULL;\n}\n\nstatic struct file_section *section_open(struct tracecmd_input *handle, int id)\n{\n\tstruct file_section *sec = section_get(handle, id);\n\n\tif (!sec)\n\t\treturn NULL;\n\n\tif (lseek(handle->fd, sec->data_offset, SEEK_SET) == (off_t)-1)\n\t\treturn NULL;\n\n\tif ((sec->flags & TRACECMD_SEC_FL_COMPRESS) && tcmd_in_uncompress_block(handle))\n\t\treturn NULL;\n\n\treturn sec;\n}\n\nstatic void section_close(struct tracecmd_input *handle, struct file_section *sec)\n{\n\tif (sec->flags & TRACECMD_SEC_FL_COMPRESS)\n\t\ttcmd_in_uncompress_reset(handle);\n}\n\nstatic int section_add_or_update(struct tracecmd_input *handle, int id, int flags,\n\t\t\t\t unsigned long long section_offset,\n\t\t\t\t unsigned long long data_offset)\n{\n\tstruct file_section *sec = section_get(handle, id);\n\n\tif (!sec) {\n\t\tsec = calloc(1, sizeof(struct file_section));\n\t\tif (!sec)\n\t\t\treturn -1;\n\t\tsec->next = handle->sections;\n\t\thandle->sections = sec;\n\t\tsec->id = id;\n\t}\n\n\tif (section_offset)\n\t\tsec->section_offset = section_offset;\n\tif (data_offset)\n\t\tsec->data_offset = data_offset;\n\tif (flags >= 0)\n\t\tsec->flags = flags;\n\n\treturn 0;\n}\n\nstatic int read_header_files(struct tracecmd_input *handle)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tunsigned long long size;\n\tchar *header;\n\tchar buf[BUFSIZ];\n\n\tif (CHECK_READ_STATE(handle, TRACECMD_FILE_HEADERS))\n\t\treturn 0;\n\n\tif (!HAS_SECTIONS(handle))\n\t\tsection_add_or_update(handle, TRACECMD_OPTION_HEADER_INFO, 0, 0,\n\t\t\t\t      lseek(handle->fd, 0, SEEK_CUR));\n\n\tif (do_read_check(handle, buf, 12))\n\t\treturn -1;\n\n\tif (memcmp(buf, \"header_page\", 12) != 0)\n\t\treturn -1;\n\n\tif (read8(handle, &size) < 0)\n\t\treturn -1;\n\n\theader = malloc(size);\n\tif (!header)\n\t\treturn -1;\n\n\tif (do_read_check(handle, header, size))\n\t\tgoto failed_read;\n\n\ttep_parse_header_page(pevent, header, size, handle->long_size);\n\tfree(header);\n\n\t/*\n\t * The size field in the page is of type long,\n\t * use that instead, since it represents the kernel.\n\t */\n\thandle->long_size = tep_get_header_page_size(pevent);\n\n\tif (do_read_check(handle, buf, 13))\n\t\treturn -1;\n\n\tif (memcmp(buf, \"header_event\", 13) != 0)\n\t\treturn -1;\n\n\tif (read8(handle, &size) < 0)\n\t\treturn -1;\n\n\theader = malloc(size);\n\tif (!header)\n\t\treturn -1;\n\n\tif (do_read_check(handle, header, size))\n\t\tgoto failed_read;\n\n\tfree(header);\n\n\thandle->file_state = TRACECMD_FILE_HEADERS;\n\n\treturn 0;\n\n failed_read:\n\tfree(header);\n\treturn -1;\n}\n\nstatic int regex_event_buf(const char *file, int size, regex_t *epreg)\n{\n\tchar *buf;\n\tchar *line;\n\tint ret;\n\n\tbuf = malloc(size + 1);\n\tif (!buf) {\n\t\ttracecmd_warning(\"Insufficient memory\");\n\t\treturn 0;\n\t}\n\n\tstrncpy(buf, file, size);\n\tbuf[size] = 0;\n\n\t/* get the name from the first line */\n\tline = strtok(buf, \"\\n\");\n\tif (!line) {\n\t\ttracecmd_warning(\"No newline found in '%s'\", buf);\n\t\tfree(buf);\n\t\treturn 0;\n\t}\n\t/* skip name if it is there */\n\tif (strncmp(line, \"name: \", 6) == 0)\n\t\tline += 6;\n\n\tret = regexec(epreg, line, 0, NULL, 0) == 0;\n\n\tfree(buf);\n\n\treturn ret;\n}\n\nstatic int read_ftrace_file(struct tracecmd_input *handle,\n\t\t\t    unsigned long long size,\n\t\t\t    int print, regex_t *epreg)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tchar *buf;\n\n\tbuf = malloc(size);\n\tif (!buf)\n\t\treturn -1;\n\tif (do_read_check(handle, buf, size)) {\n\t\tfree(buf);\n\t\treturn -1;\n\t}\n\n\tif (epreg) {\n\t\tif (print || regex_event_buf(buf, size, epreg))\n\t\t\tprintf(\"%.*s\\n\", (int)size, buf);\n\t} else {\n\t\tif (tep_parse_event(pevent, buf, size, \"ftrace\"))\n\t\t\thandle->parsing_failures++;\n\t}\n\tfree(buf);\n\n\treturn 0;\n}\n\nstatic int read_event_file(struct tracecmd_input *handle,\n\t\t\t   char *system, unsigned long long size,\n\t\t\t   int print, int *sys_printed,\n\t\t\t   regex_t *epreg)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tchar *buf;\n\n\tbuf = malloc(size);\n\tif (!buf)\n\t\treturn -1;\n\n\tif (do_read_check(handle, buf, size)) {\n\t\tfree(buf);\n\t\treturn -1;\n\t}\n\n\tif (epreg) {\n\t\tif (print || regex_event_buf(buf, size, epreg)) {\n\t\t\tif (!*sys_printed) {\n\t\t\t\tprintf(\"\\nsystem: %s\\n\", system);\n\t\t\t\t*sys_printed = 1;\n\t\t\t}\n\t\t\tprintf(\"%.*s\\n\", (int)size, buf);\n\t\t}\n\t} else {\n\t\tif (tep_parse_event(pevent, buf, size, system))\n\t\t\thandle->parsing_failures++;\n\t}\n\tfree(buf);\n\n\treturn 0;\n}\n\nstatic int make_preg_files(const char *regex, regex_t *system,\n\t\t\t   regex_t *event, int *unique)\n{\n\tchar *buf;\n\tchar *sstr;\n\tchar *estr;\n\tint ret;\n\n\t/* unique is set if a colon is found */\n\t*unique = 0;\n\n\t/* split \"system:event\" into \"system\" and \"event\" */\n\n\tbuf = strdup(regex);\n\tif (!buf)\n\t\treturn -ENOMEM;\n\n\tsstr = strtok(buf, \":\");\n\testr = strtok(NULL, \":\");\n\n\t/* If no colon is found, set event == system */\n\tif (!estr)\n\t\testr = sstr;\n\telse\n\t\t*unique = 1;\n\n\tret = regcomp(system, sstr, REG_ICASE|REG_NOSUB);\n\tif (ret) {\n\t\ttracecmd_warning(\"Bad regular expression '%s'\", sstr);\n\t\tgoto out;\n\t}\n\n\tret = regcomp(event, estr, REG_ICASE|REG_NOSUB);\n\tif (ret) {\n\t\ttracecmd_warning(\"Bad regular expression '%s'\", estr);\n\t\tgoto out;\n\t}\n\n out:\n\tfree(buf);\n\treturn ret;\n}\n\nstatic int read_ftrace_files(struct tracecmd_input *handle, const char *regex)\n{\n\tunsigned long long size;\n\tregex_t spreg;\n\tregex_t epreg;\n\tregex_t *sreg = NULL;\n\tregex_t *ereg = NULL;\n\tunsigned int count, i;\n\tint print_all = 0;\n\tint unique;\n\tint ret;\n\n\tif (CHECK_READ_STATE(handle, TRACECMD_FILE_FTRACE_EVENTS))\n\t\treturn 0;\n\n\tif (!HAS_SECTIONS(handle))\n\t\tsection_add_or_update(handle, TRACECMD_OPTION_FTRACE_EVENTS, 0, 0,\n\t\t\t\t      lseek(handle->fd, 0, SEEK_CUR));\n\n\tif (regex) {\n\t\tsreg = &spreg;\n\t\tereg = &epreg;\n\t\tret = make_preg_files(regex, sreg, ereg, &unique);\n\t\tif (ret)\n\t\t\treturn -1;\n\n\t\tif (regexec(sreg, \"ftrace\", 0, NULL, 0) == 0) {\n\t\t\t/*\n\t\t\t * If the system matches a regex that did\n\t\t\t * not contain a colon, then print all events.\n\t\t\t */\n\t\t\tif (!unique)\n\t\t\t\tprint_all = 1;\n\t\t} else if (unique) {\n\t\t\t/*\n\t\t\t * The user specified a unique event that did\n\t\t\t * not match the ftrace system. Don't print any\n\t\t\t * events here.\n\t\t\t */\n\t\t\tregfree(sreg);\n\t\t\tregfree(ereg);\n\t\t\tsreg = NULL;\n\t\t\tereg = NULL;\n\t\t}\n\t}\n\n\tret = read4(handle, &count);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tfor (i = 0; i < count; i++) {\n\t\tret = read8(handle, &size);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\t\tret = read_ftrace_file(handle, size, print_all, ereg);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\t}\n\n\thandle->file_state = TRACECMD_FILE_FTRACE_EVENTS;\n\tret = 0;\nout:\n\tif (sreg) {\n\t\tregfree(sreg);\n\t\tregfree(ereg);\n\t}\n\n\treturn ret;\n}\n\nstatic int read_event_files(struct tracecmd_input *handle, const char *regex)\n{\n\tunsigned long long size;\n\tchar *system = NULL;\n\tregex_t spreg;\n\tregex_t epreg;\n\tregex_t *sreg = NULL;\n\tregex_t *ereg = NULL;\n\tregex_t *reg;\n\tunsigned int systems;\n\tunsigned int count;\n\tunsigned int i, x;\n\tint print_all;\n\tint sys_printed;\n\tint unique;\n\tint ret;\n\n\tif (CHECK_READ_STATE(handle, TRACECMD_FILE_ALL_EVENTS))\n\t\treturn 0;\n\n\tif (!HAS_SECTIONS(handle))\n\t\tsection_add_or_update(handle, TRACECMD_OPTION_EVENT_FORMATS, 0, 0,\n\t\t\t\t      lseek(handle->fd, 0, SEEK_CUR));\n\n\tif (regex) {\n\t\tsreg = &spreg;\n\t\tereg = &epreg;\n\t\tret = make_preg_files(regex, sreg, ereg, &unique);\n\t\tif (ret)\n\t\t\treturn -1;\n\t}\n\n\tret = read4(handle, &systems);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tfor (i = 0; i < systems; i++) {\n\t\tsystem = read_string(handle);\n\t\tif (!system) {\n\t\t\tret = -1;\n\t\t\tgoto out;\n\t\t}\n\n\t\tsys_printed = 0;\n\t\tprint_all = 0;\n\t\treg = ereg;\n\n\t\tif (sreg) {\n\t\t\tif (regexec(sreg, system, 0, NULL, 0) == 0) {\n\t\t\t\t/*\n\t\t\t\t * If the user passed in a regex that\n\t\t\t\t * did not contain a colon, then we can\n\t\t\t\t * print all the events of this system.\n\t\t\t\t */\n\t\t\t\tif (!unique)\n\t\t\t\t\tprint_all = 1;\n\t\t\t} else if (unique) {\n\t\t\t\t/*\n\t\t\t\t * The user passed in a unique event that\n\t\t\t\t * specified a specific system and event.\n\t\t\t\t * Since this system doesn't match this\n\t\t\t\t * event, then we don't print any events\n\t\t\t\t * for this system.\n\t\t\t\t */\n\t\t\t\treg = NULL;\n\t\t\t}\n\t\t}\n\n\t\tret = read4(handle, &count);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\n\t\tfor (x=0; x < count; x++) {\n\t\t\tret = read8(handle, &size);\n\t\t\tif (ret < 0)\n\t\t\t\tgoto out;\n\n\t\t\tret = read_event_file(handle, system, size,\n\t\t\t\t\t      print_all, &sys_printed,\n\t\t\t\t\t      reg);\n\t\t\tif (ret < 0)\n\t\t\t\tgoto out;\n\t\t}\n\t\tfree(system);\n\t}\n\tsystem = NULL;\n\n\thandle->file_state = TRACECMD_FILE_ALL_EVENTS;\n\tret = 0;\n out:\n\tif (sreg) {\n\t\tregfree(sreg);\n\t\tregfree(ereg);\n\t}\n\n\tfree(system);\n\treturn ret;\n}\n\nstatic int read_proc_kallsyms(struct tracecmd_input *handle)\n{\n\tstruct tep_handle *tep = handle->pevent;\n\tunsigned int size;\n\tchar *buf;\n\n\tif (CHECK_READ_STATE(handle, TRACECMD_FILE_KALLSYMS))\n\t\treturn 0;\n\tif (!HAS_SECTIONS(handle))\n\t\tsection_add_or_update(handle, TRACECMD_OPTION_KALLSYMS, 0, 0,\n\t\t\t\t      lseek(handle->fd, 0, SEEK_CUR));\n\n\tif (read4(handle, &size) < 0)\n\t\treturn -1;\n\tif (!size) {\n\t\thandle->file_state = TRACECMD_FILE_KALLSYMS;\n\t\treturn 0; /* OK? */\n\t}\n\n\tbuf = malloc(size+1);\n\tif (!buf)\n\t\treturn -1;\n\tif (do_read_check(handle, buf, size)){\n\t\tfree(buf);\n\t\treturn -1;\n\t}\n\tbuf[size] = 0;\n\n\ttep_parse_kallsyms(tep, buf);\n\n\tfree(buf);\n\n\thandle->file_state = TRACECMD_FILE_KALLSYMS;\n\n\treturn 0;\n}\n\nstatic int read_ftrace_printk(struct tracecmd_input *handle)\n{\n\tunsigned int size;\n\tchar *buf;\n\n\tif (CHECK_READ_STATE(handle, TRACECMD_FILE_PRINTK))\n\t\treturn 0;\n\n\tif (!HAS_SECTIONS(handle))\n\t\tsection_add_or_update(handle, TRACECMD_OPTION_PRINTK, 0, 0,\n\t\t\t\t      lseek(handle->fd, 0, SEEK_CUR));\n\n\tif (read4(handle, &size) < 0)\n\t\treturn -1;\n\tif (!size) {\n\t\thandle->file_state = TRACECMD_FILE_PRINTK;\n\t\treturn 0; /* OK? */\n\t}\n\n\tbuf = malloc(size + 1);\n\tif (!buf)\n\t\treturn -1;\n\tif (do_read_check(handle, buf, size)) {\n\t\tfree(buf);\n\t\treturn -1;\n\t}\n\n\tbuf[size] = 0;\n\n\ttep_parse_printk_formats(handle->pevent, buf);\n\n\tfree(buf);\n\n\thandle->file_state = TRACECMD_FILE_PRINTK;\n\n\treturn 0;\n}\n\nstatic int read_btf(struct tracecmd_input *handle)\n{\n\tvoid *raw_data;\n\tsize_t size;\n\n\traw_data = tracecmd_uncompress_buffer(handle->compress, &size);\n\tif (!raw_data)\n\t\treturn -1;\n\n\ttep_load_btf(handle->pevent, raw_data, size);\n\n\tfree(raw_data);\n\treturn 0;\n}\n\nstatic int read_modules(struct tracecmd_input *handle)\n{\n\tchar *modules;\n\tsize_t size;\n\n\tmodules = tracecmd_uncompress_buffer(handle->compress, &size);\n\tif (!modules)\n\t\treturn -1;\n\n\ttep_load_modules(handle->pevent, modules, size);\n\n\tfree(modules);\n\treturn 0;\n}\n\nstatic int read_and_parse_cmdlines(struct tracecmd_input *handle);\n\n/**\n * tracecmd_get_parsing_failures - get the count of parsing failures\n * @handle: input handle for the trace.dat file\n *\n * This returns the count of failures while parsing the event files\n */\nint tracecmd_get_parsing_failures(struct tracecmd_input *handle)\n{\n\tif (handle)\n\t\treturn handle->parsing_failures;\n\treturn 0;\n}\n\nstatic int read_cpus(struct tracecmd_input *handle)\n{\n\tunsigned int cpus;\n\n\tif (CHECK_READ_STATE(handle, TRACECMD_FILE_CPU_COUNT))\n\t\treturn 0;\n\n\tif (read4(handle, &cpus) < 0)\n\t\treturn -1;\n\n\thandle->cpus = cpus;\n\thandle->max_cpu = cpus;\n\ttep_set_cpus(handle->pevent, handle->cpus);\n\thandle->file_state = TRACECMD_FILE_CPU_COUNT;\n\n\treturn 0;\n}\n\nstatic int read_headers_v6(struct tracecmd_input *handle, enum tracecmd_file_states state,\n\t\t\t   const char *regex)\n{\n\tint ret;\n\n\t/* Set to read all if state is zero */\n\tif (!state)\n\t\tstate = TRACECMD_FILE_OPTIONS;\n\n\tif (state <= handle->file_state)\n\t\treturn 0;\n\n\thandle->parsing_failures = 0;\n\n\tret = read_header_files(handle);\n\tif (ret < 0)\n\t\treturn -1;\n\n\tif (state <= handle->file_state)\n\t\treturn 0;\n\n\tret = read_ftrace_files(handle, NULL);\n\tif (ret < 0)\n\t\treturn -1;\n\n\tif (state <= handle->file_state)\n\t\treturn 0;\n\n\tret = read_event_files(handle, regex);\n\tif (ret < 0)\n\t\treturn -1;\n\n\tif (state <= handle->file_state)\n\t\treturn 0;\n\n\tret = read_proc_kallsyms(handle);\n\tif (ret < 0)\n\t\treturn -1;\n\n\tif (state <= handle->file_state)\n\t\treturn 0;\n\n\tret = read_ftrace_printk(handle);\n\tif (ret < 0)\n\t\treturn -1;\n\n\tif (state <= handle->file_state)\n\t\treturn 0;\n\n\tif (read_and_parse_cmdlines(handle) < 0)\n\t\treturn -1;\n\n\tif (state <= handle->file_state)\n\t\treturn 0;\n\n\tif (read_cpus(handle) < 0)\n\t\treturn -1;\n\n\tif (state <= handle->file_state)\n\t\treturn 0;\n\n\tif (read_options_type(handle) < 0)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic int handle_options(struct tracecmd_input *handle);\n\nstatic const char *get_metadata_string(struct tracecmd_input *handle, int offset)\n{\n\tif (!handle || !handle->strings || offset < 0 || handle->strings_size >= offset)\n\t\treturn NULL;\n\n\treturn handle->strings + offset;\n}\n\nstatic int read_section_header(struct tracecmd_input *handle, unsigned short *id,\n\t\t\t       unsigned short *flags, unsigned long long *size, const char **description)\n{\n\tunsigned short fl;\n\tunsigned short sec_id;\n\tunsigned long long sz;\n\tint desc;\n\n\tif (read2(handle, &sec_id))\n\t\treturn -1;\n\tif (read2(handle, &fl))\n\t\treturn -1;\n\tif (read4(handle, (unsigned int *)&desc))\n\t\treturn -1;\n\tif (read8(handle, &sz))\n\t\treturn -1;\n\n\tif (id)\n\t\t*id = sec_id;\n\tif (flags)\n\t\t*flags = fl;\n\tif (size)\n\t\t*size = sz;\n\tif (description)\n\t\t*description = get_metadata_string(handle, desc);\n\n\treturn 0;\n}\n\nstatic int handle_section(struct tracecmd_input *handle, struct file_section *section,\n\t\t\t  const char *regex)\n{\n\tunsigned short id, flags;\n\tunsigned long long size;\n\tint ret;\n\n\tif (lseek(handle->fd, section->section_offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\tif (read_section_header(handle, &id, &flags, &size, NULL))\n\t\treturn -1;\n\tsection->flags = flags;\n\tif (id != section->id)\n\t\treturn -1;\n\n\tsection->data_offset = lseek(handle->fd, 0, SEEK_CUR);\n\tif ((section->flags & TRACECMD_SEC_FL_COMPRESS) && tcmd_in_uncompress_block(handle))\n\t\treturn -1;\n\n\tswitch (section->id) {\n\tcase TRACECMD_OPTION_HEADER_INFO:\n\t\tret = read_header_files(handle);\n\t\tbreak;\n\tcase TRACECMD_OPTION_FTRACE_EVENTS:\n\t\tret = read_ftrace_files(handle, NULL);\n\t\tbreak;\n\tcase TRACECMD_OPTION_EVENT_FORMATS:\n\t\tret = read_event_files(handle, regex);\n\t\tbreak;\n\tcase TRACECMD_OPTION_KALLSYMS:\n\t\tret = read_proc_kallsyms(handle);\n\t\tbreak;\n\tcase TRACECMD_OPTION_PRINTK:\n\t\tret = read_ftrace_printk(handle);\n\t\tbreak;\n\tcase TRACECMD_OPTION_CMDLINES:\n\t\tret = read_and_parse_cmdlines(handle);\n\t\tbreak;\n\tcase TRACECMD_OPTION_BTF_FILE:\n\t\tret = read_btf(handle);\n\t\tbreak;\n\tcase TRACECMD_OPTION_MODULES_FILE:\n\t\tret = read_modules(handle);\n\t\tbreak;\n\tdefault:\n\t\tret = 0;\n\t\tbreak;\n\t}\n\n\tif (section->flags & TRACECMD_SEC_FL_COMPRESS)\n\t\ttcmd_in_uncompress_reset(handle);\n\n\treturn ret;\n}\n\nstatic int read_headers(struct tracecmd_input *handle, const char *regex)\n{\n\tstruct file_section *section;\n\n\tif (handle->options_init)\n\t\treturn 0;\n\n\tif (!handle->options_start)\n\t\treturn -1;\n\n\tif (lseek(handle->fd, handle->options_start, SEEK_SET) == (off_t)-1) {\n\t\ttracecmd_warning(\"Filed to goto options offset %lld\", handle->options_start);\n\t\treturn -1;\n\t}\n\n\tif (handle_options(handle))\n\t\treturn -1;\n\n\tsection = handle->sections;\n\twhile (section) {\n\t\tif (handle_section(handle, section, NULL))\n\t\t\treturn -1;\n\t\tsection = section->next;\n\t}\n\n\thandle->options_init = true;\n\treturn 0;\n}\n\n/**\n * tracecmd_read_headers - read the header information from trace.dat\n * @handle: input handle for the trace.dat file\n * @state: The state to read up to or zero to read up to options.\n *\n * This reads the trace.dat file for various information. Like the\n * format of the ring buffer, event formats, ftrace formats, kallsyms\n * and printk. This may be called multiple times with different @state\n * values, to read partial data at a time. It will always continue\n * where it left off.\n */\nint tracecmd_read_headers(struct tracecmd_input *handle,\n\t\t\t  enum tracecmd_file_states state)\n{\n\tif (!HAS_SECTIONS(handle))\n\t\treturn read_headers_v6(handle, state, NULL);\n\treturn read_headers(handle, NULL);\n}\n\nstatic unsigned long long calc_page_offset(struct tracecmd_input *handle,\n\t\t\t\t\t   unsigned long long offset)\n{\n\treturn offset & ~(handle->page_size - 1);\n}\n\nstatic int read_page(struct tracecmd_input *handle, off_t offset,\n\t\t     int cpu, void *map)\n{\n\toff_t save_seek;\n\toff_t ret;\n\n\tif (handle->use_pipe) {\n\t\tret = read(handle->cpu_data[cpu].pipe_fd, map, handle->page_size);\n\t\t/* Set EAGAIN if the pipe is empty */\n\t\tif (ret < 0) {\n\t\t\terrno = EAGAIN;\n\t\t\treturn -1;\n\n\t\t} else if (ret == 0) {\n\t\t\t/* Set EINVAL when the pipe has closed */\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/* other parts of the code may expect the pointer to not move */\n\tsave_seek = lseek(handle->fd, 0, SEEK_CUR);\n\n\tret = lseek(handle->fd, offset, SEEK_SET);\n\tif (ret < 0)\n\t\treturn -1;\n\tret = read(handle->fd, map, handle->page_size);\n\tif (ret < 0)\n\t\treturn -1;\n\n\t/* reset the file pointer back */\n\tlseek(handle->fd, save_seek, SEEK_SET);\n\n\treturn 0;\n}\n\n/* page_map_size must be a power of two */\nstatic unsigned long long normalize_size(unsigned long long size)\n{\n\t/* From Hacker's Delight: or bits after first set bit to all 1s */\n\tsize |= (size >> 1);\n\tsize |= (size >> 2);\n\tsize |= (size >> 4);\n\tsize |= (size >> 8);\n\tsize |= (size >> 16);\n\tsize |= (size >> 32);\n\n\t/* Clear all bits except first one for previous power of two */\n\treturn size - (size >> 1);\n}\n\nstatic void free_page_map(struct page_map *page_map)\n{\n\tpage_map->ref_count--;\n\tif (page_map->ref_count)\n\t\treturn;\n\n\tmunmap(page_map->map, page_map->size);\n\tlist_del(&page_map->list);\n\tfree(page_map);\n}\n\n#define CHUNK_CHECK_OFFSET(C, O)\t((O) >= (C)->offset && (O) < ((C)->offset + (C)->size))\n\nstatic int chunk_cmp(const void *A, const void *B)\n{\n\tconst struct tracecmd_compress_chunk *a = A;\n\tconst struct tracecmd_compress_chunk *b = B;\n\n\tif (CHUNK_CHECK_OFFSET(b, a->offset))\n\t\treturn 0;\n\n\tif (a->offset < b->offset)\n\t\treturn -1;\n\n\treturn 1;\n}\n\nstatic struct tracecmd_compress_chunk *get_zchunk(struct cpu_data *cpu, off_t offset)\n{\n\tstruct cpu_zdata *cpuz = &cpu->compress;\n\tstruct tracecmd_compress_chunk *chunk;\n\tstruct tracecmd_compress_chunk key;\n\n\tif (!cpuz->chunks)\n\t\treturn NULL;\n\n\tif (offset > (cpuz->chunks[cpuz->count - 1].offset + cpuz->chunks[cpuz->count - 1].size))\n\t\treturn NULL;\n\n\t/* check if the requested offset is in the last requested chunk or in the next chunk */\n\tif (CHUNK_CHECK_OFFSET(cpuz->chunks + cpuz->last_chunk, offset))\n\t\treturn cpuz->chunks + cpuz->last_chunk;\n\n\tcpuz->last_chunk++;\n\tif (cpuz->last_chunk < cpuz->count &&\n\t    CHUNK_CHECK_OFFSET(cpuz->chunks + cpuz->last_chunk, offset))\n\t\treturn cpuz->chunks + cpuz->last_chunk;\n\n\tkey.offset = offset;\n\tchunk = bsearch(&key, cpuz->chunks, cpuz->count, sizeof(*chunk), chunk_cmp);\n\n\tif (!chunk) /* should never happen */\n\t\treturn NULL;\n\n\tcpuz->last_chunk = chunk - cpuz->chunks;\n\treturn chunk;\n}\n\nstatic void free_zpage(struct cpu_data *cpu_data, off_t offset)\n{\n\tstruct trace_rbtree_node *node;\n\tstruct zchunk_cache *cache;\n\n\toffset -= cpu_data->file_offset;\n\n\tnode = trace_rbtree_find(&cpu_data->compress.cache, (void *)&offset);\n\n\tif (!node)\n\t\treturn;\n\n\tcache = container_of(node, struct zchunk_cache, node);\n\n\tcache->ref--;\n\tif (cache->ref)\n\t\treturn;\n\n\ttrace_rbtree_delete(&cpu_data->compress.cache, node);\n\n\tfree(cache->map);\n\tfree(cache);\n}\n\nstatic void *read_zpage(struct tracecmd_input *handle, int cpu, off_t offset)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\tstruct tracecmd_compress_chunk *chunk;\n\tstruct trace_rbtree_node *node;\n\tstruct zchunk_cache *cache;\n\tvoid *map = NULL;\n\tint pindex;\n\tint size;\n\n\toffset -= cpu_data->file_offset;\n\n\t/* Look in the cache of already loaded chunks */\n\tnode = trace_rbtree_find(&cpu_data->compress.cache, (void *)&offset);\n\tif (node) {\n\t\tcache = container_of(node, struct zchunk_cache, node);\n\t\tcache->ref++;\n\t\tgoto out;\n\t}\n\n\tchunk =  get_zchunk(cpu_data, offset);\n\tif (!chunk)\n\t\treturn NULL;\n\n\tsize = handle->page_size > chunk->size ? handle->page_size : chunk->size;\n\tmap = malloc(size);\n\tif (!map)\n\t\treturn NULL;\n\n\tif (tracecmd_uncompress_chunk(handle->compress, chunk, map) < 0)\n\t\tgoto error;\n\n\tcache = calloc(1, sizeof(struct zchunk_cache));\n\tif (!cache)\n\t\tgoto error;\n\n\tcache->ref = 1;\n\tcache->chunk = chunk;\n\tcache->map = map;\n\ttcmd_rbtree_insert(&cpu_data->compress.cache, &cache->node);\n\n\t/* a chunk can hold multiple pages, get the requested one */\nout:\n\tpindex = (offset - cache->chunk->offset) / handle->page_size;\n\treturn cache->map + (pindex * handle->page_size);\nerror:\n\tfree(map);\n\treturn NULL;\n}\n\nstatic void *allocate_page_map(struct tracecmd_input *handle,\n\t\t\t       struct page *page, int cpu, off_t offset)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\tstruct page_map *page_map;\n\toff_t map_size;\n\toff_t map_offset;\n\tvoid *map;\n\tint ret;\n\tint fd;\n\n\tif (handle->cpu_compressed) {\n\t\tif (handle->read_zpage)\n\t\t\treturn read_zpage(handle, cpu, offset);\n\t\toffset -= cpu_data->file_offset;\n\t}\n\n\tif (handle->read_page) {\n\t\tmap = malloc(handle->page_size);\n\t\tif (!map)\n\t\t\treturn NULL;\n\t\tret = read_page(handle, offset, cpu, map);\n\t\tif (ret < 0) {\n\t\t\tfree(map);\n\t\t\treturn NULL;\n\t\t}\n\t\treturn map;\n\t}\n\n\tmap_size = handle->page_map_size;\n\tmap_offset = offset & ~(map_size - 1);\n\n\tif (!handle->cpu_compressed && map_offset < cpu_data->file_offset) {\n\t\tmap_size -= cpu_data->file_offset - map_offset;\n\t\tmap_offset = cpu_data->file_offset;\n\t}\n\n\tpage_map = cpu_data->page_map;\n\n\tif (page_map && page_map->offset == map_offset)\n\t\tgoto out;\n\n\tlist_for_each_entry(page_map, &cpu_data->page_maps, list) {\n\t\tif (page_map->offset == map_offset)\n\t\t\tgoto out;\n\t}\n\n\tpage_map = calloc(1, sizeof(*page_map));\n\tif (!page_map)\n\t\treturn NULL;\n\n\tif (map_offset + map_size > cpu_data->file_offset + cpu_data->file_size)\n\t\tmap_size -= map_offset + map_size -\n\t\t\t(cpu_data->file_offset + cpu_data->file_size);\n\n\tif (cpu_data->compress.fd >= 0)\n\t\tfd = cpu_data->compress.fd;\n\telse\n\t\tfd = handle->fd;\n again:\n\tpage_map->size = map_size;\n\tpage_map->offset = map_offset;\n\n\tpage_map->map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, map_offset);\n\n\tif (page_map->map == MAP_FAILED) {\n\t\t/* Try a smaller map */\n\t\tmap_size >>= 1;\n\t\tif (map_size < handle->page_size) {\n\t\t\tfree(page_map);\n\t\t\treturn NULL;\n\t\t}\n\t\thandle->page_map_size = map_size;\n\t\tmap_offset = offset & ~(map_size - 1);\n\t\t/*\n\t\t * Note, it is now possible to get duplicate memory\n\t\t * maps. But that's fine, the previous maps with\n\t\t * larger sizes will eventually be unmapped.\n\t\t */\n\t\tgoto again;\n\t}\n\n\tlist_add(&page_map->list, &cpu_data->page_maps);\n out:\n\tif (cpu_data->page_map != page_map) {\n\t\tstruct page_map *old_map = cpu_data->page_map;\n\t\tcpu_data->page_map = page_map;\n\t\tpage_map->ref_count++;\n\t\tif (old_map)\n\t\t\tfree_page_map(old_map);\n\t}\n\tpage->page_map = page_map;\n\tpage_map->ref_count++;\n\treturn page_map->map + offset - page_map->offset;\n}\n\nstatic struct page *allocate_page(struct tracecmd_input *handle,\n\t\t\t\t  int cpu, off_t offset)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\tstruct page **pages;\n\tstruct page *page;\n\tint index;\n\n\tindex = (offset - cpu_data->file_offset) / handle->page_size;\n\tif (index >= cpu_data->nr_pages) {\n\t\tpages = realloc(cpu_data->pages, (index + 1) * sizeof(*cpu_data->pages));\n\t\tif (!pages)\n\t\t\treturn NULL;\n\t\tmemset(pages + cpu_data->nr_pages, 0,\n\t\t       (index + 1 - cpu_data->nr_pages) * sizeof(*cpu_data->pages));\n\t\tcpu_data->pages = pages;\n\t\tcpu_data->nr_pages = index + 1;\n\t}\n\tif (cpu_data->pages[index]) {\n\t\tcpu_data->pages[index]->ref_count++;\n\t\treturn cpu_data->pages[index];\n\t}\n\n\tpage = malloc(sizeof(*page));\n\tif (!page)\n\t\treturn NULL;\n\n\tmemset(page, 0, sizeof(*page));\n\tpage->offset = offset;\n\tpage->handle = handle;\n\tpage->cpu = cpu;\n\n\tpage->map = allocate_page_map(handle, page, cpu, offset);\n\n\tif (!page->map) {\n\t\tfree(page);\n\t\treturn NULL;\n\t}\n\n\tcpu_data->pages[index] = page;\n\tcpu_data->page_cnt++;\n\tpage->ref_count = 1;\n\n\treturn page;\n}\n\nstatic void __free_page(struct tracecmd_input *handle, struct page *page)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[page->cpu];\n\tstruct page **pages;\n\tint index;\n\n\tif (!page->ref_count) {\n\t\ttracecmd_critical(\"Page ref count is zero!\");\n\t\treturn;\n\t}\n\n\tpage->ref_count--;\n\tif (page->ref_count)\n\t\treturn;\n\n\tif (handle->read_page)\n\t\tfree(page->map);\n\telse if (handle->read_zpage)\n\t\tfree_zpage(cpu_data, page->offset);\n\telse\n\t\tfree_page_map(page->page_map);\n\n\tindex = (page->offset - cpu_data->file_offset) / handle->page_size;\n\tcpu_data->pages[index] = NULL;\n\tcpu_data->page_cnt--;\n\n\tfree(page);\n\n\tif (handle->use_pipe) {\n\t\tfor (index = cpu_data->nr_pages - 1; index > 0; index--)\n\t\t\tif (cpu_data->pages[index])\n\t\t\t\tbreak;\n\t\tif (index < (cpu_data->nr_pages - 1)) {\n\t\t\tpages = realloc(cpu_data->pages, (index + 1) * sizeof(*cpu_data->pages));\n\t\t\tif (!pages)\n\t\t\t\treturn;\n\t\t\tcpu_data->pages = pages;\n\t\t\tcpu_data->nr_pages = index + 1;\n\t\t}\n\t}\n}\n\nstatic void free_page(struct tracecmd_input *handle, int cpu)\n{\n\tif (!handle->cpu_data || cpu >= handle->cpus ||\n\t    !handle->cpu_data[cpu].page)\n\t\treturn;\n\n\t__free_page(handle, handle->cpu_data[cpu].page);\n\n\thandle->cpu_data[cpu].page = NULL;\n}\n\nstatic void __free_record(struct tep_record *record)\n{\n\tif (record->priv) {\n\t\tstruct page *page = record->priv;\n\t\tremove_record(page, record);\n\t\t__free_page(page->handle, page);\n\t}\n\n\tfree(record);\n}\n\nvoid tracecmd_free_record(struct tep_record *record)\n{\n\tif (!record)\n\t\treturn;\n\n\tif (!record->ref_count) {\n\t\ttracecmd_critical(\"record ref count is zero!\");\n\t\treturn;\n\t}\n\n\trecord->ref_count--;\n\n\tif (record->ref_count)\n\t\treturn;\n\n\tif (record->locked) {\n\t\ttracecmd_critical(\"freeing record when it is locked!\");\n\t\treturn;\n\t}\n\n\trecord->data = NULL;\n\n\t__free_record(record);\n}\n\nvoid tracecmd_record_ref(struct tep_record *record)\n{\n\trecord->ref_count++;\n#if DEBUG_RECORD\n\t/* Update locating of last reference */\n\trecord->alloc_addr = (unsigned long)__builtin_return_address(0);\n#endif\n}\n\nstatic void free_next(struct tracecmd_input *handle, int cpu)\n{\n\tstruct tep_record *record;\n\n\tif (!handle->cpu_data || cpu >= handle->cpus)\n\t\treturn;\n\n\trecord = handle->cpu_data[cpu].next;\n\tif (!record)\n\t\treturn;\n\n\thandle->cpu_data[cpu].next = NULL;\n\n\trecord->locked = 0;\n\ttracecmd_free_record(record);\n}\n\n/* This functions was taken from the Linux kernel */\nstatic unsigned long long mul_u64_u32_shr(unsigned long long a,\n\t\t\t\t\t  unsigned long long mul, unsigned int shift)\n{\n\tunsigned int ah, al;\n\tunsigned long long ret;\n\n\tal = a;\n\tah = a >> 32;\n\n\tret = (al * mul) >> shift;\n\tif (ah)\n\t\tret += (ah * mul) << (32 - shift);\n\n\treturn ret;\n}\n\nstatic inline unsigned long long\ntimestamp_correction_calc(unsigned long long ts, unsigned int flags,\n\t\t\t  struct ts_offset_sample *min,\n\t\t\t  struct ts_offset_sample *max)\n{\n\tlong long tscor;\n\n\tif (flags & TRACECMD_TSYNC_FLAG_INTERPOLATE) {\n\t\tlong long delta = max->time - min->time;\n\t\tlong long offset = ((long long)ts - min->time) *\n\t\t\t\t   (max->offset - min->offset);\n\n\t\ttscor = min->offset + (offset + delta / 2) / delta;\n\t} else {\n\t\ttscor = min->offset;\n\t}\n\n\tts = (ts * min->scaling) >> min->fraction;\n\tif (tscor < 0)\n\t\treturn ts - llabs(tscor);\n\n\treturn ts + tscor;\n}\n\nstatic unsigned long long timestamp_host_sync(unsigned long long ts, int cpu,\n\t\t\t\t\t      struct tracecmd_input *handle)\n{\n\tstruct timesync_offsets *tsync;\n\tint min, mid, max;\n\n\tif (cpu >= handle->host.cpu_count)\n\t\treturn ts;\n\ttsync = &handle->host.ts_offsets[cpu];\n\n\t/* We have one sample, nothing to calc here */\n\tif (tsync->ts_samples_count == 1)\n\t\treturn ts + tsync->ts_samples[0].offset;\n\n\t/* We have two samples, nothing to search here */\n\tif (tsync->ts_samples_count == 2)\n\t\treturn timestamp_correction_calc(ts, handle->host.flags,\n\t\t\t\t\t\t &tsync->ts_samples[0],\n\t\t\t\t\t\t &tsync->ts_samples[1]);\n\n\t/* We have more than two samples */\n\tif (ts <= tsync->ts_samples[0].time)\n\t\treturn timestamp_correction_calc(ts, handle->host.flags,\n\t\t\t\t\t\t &tsync->ts_samples[0],\n\t\t\t\t\t\t &tsync->ts_samples[1]);\n\telse if (ts >= tsync->ts_samples[tsync->ts_samples_count-1].time)\n\t\treturn timestamp_correction_calc(ts, handle->host.flags,\n\t\t\t\t\t\t &tsync->ts_samples[tsync->ts_samples_count-2],\n\t\t\t\t\t\t &tsync->ts_samples[tsync->ts_samples_count-1]);\n\tmin = 0;\n\tmax = tsync->ts_samples_count-1;\n\tmid = (min + max)/2;\n\twhile (min <= max) {\n\t\tif (ts < tsync->ts_samples[mid].time)\n\t\t\tmax = mid - 1;\n\t\telse if (ts > tsync->ts_samples[mid].time)\n\t\t\tmin = mid + 1;\n\t\telse\n\t\t\tbreak;\n\t\tmid = (min + max)/2;\n\t}\n\n\treturn timestamp_correction_calc(ts, handle->host.flags,\n\t\t\t\t\t &tsync->ts_samples[mid],\n\t\t\t\t\t &tsync->ts_samples[mid+1]);\n}\n\nstatic unsigned long long timestamp_calc(unsigned long long ts, int cpu,\n\t\t\t\t\t struct tracecmd_input *handle)\n{\n\t/* do not modify raw timestamps */\n\tif (handle->flags & TRACECMD_FL_RAW_TS)\n\t\treturn ts;\n\n\t/* Guest trace file, sync with host timestamps */\n\tif (handle->host.sync_enable)\n\t\tts = timestamp_host_sync(ts, cpu, handle);\n\n\tif (handle->ts2secs) {\n\t\t/* user specified clock frequency */\n\t\tts *= handle->ts2secs;\n\t} else if (handle->tsc_calc.mult) {\n\t\t/* auto calculated TSC clock frequency */\n\t\tts = mul_u64_u32_shr(ts, handle->tsc_calc.mult, handle->tsc_calc.shift);\n\t}\n\n\t/* User specified time offset with --ts-offset or --date options */\n\tts += handle->ts_offset;\n\n\treturn ts;\n}\n\n/*\n * Page is mapped, now read in the page header info.\n */\nstatic int update_page_info(struct tracecmd_input *handle, int cpu)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tvoid *ptr = handle->cpu_data[cpu].page->map;\n\tstruct kbuffer *kbuf = handle->cpu_data[cpu].kbuf;\n\n\t/* FIXME: handle header page */\n\tif (tep_get_header_timestamp_size(pevent) != 8) {\n\t\ttracecmd_warning(\"expected a long long type for timestamp\");\n\t\treturn -1;\n\t}\n\n\tkbuffer_load_subbuffer(kbuf, ptr);\n\tif (kbuffer_subbuffer_size(kbuf) > handle->page_size) {\n\t\ttracecmd_warning(\"bad page read, with size of %d\", kbuffer_subbuffer_size(kbuf));\n\t\treturn -1;\n\t}\n\thandle->cpu_data[cpu].timestamp = timestamp_calc(kbuffer_timestamp(kbuf),\n\t\t\t\t\t\t\t cpu, handle);\n\n\treturn 0;\n}\n\n/*\n * get_page maps a page for a given cpu.\n *\n * Returns 1 if the page was already mapped,\n *         0 if it mapped successfully\n *        -1 on error\n */\nstatic int get_page(struct tracecmd_input *handle, int cpu,\n\t\t    off_t offset)\n{\n\t/* Don't map if the page is already where we want */\n\tif (handle->cpu_data[cpu].offset == offset &&\n\t    handle->cpu_data[cpu].page)\n\t\treturn 1;\n\n\t/* Do not map no data for CPU */\n\tif (!handle->cpu_data[cpu].size)\n\t\treturn -1;\n\n\tif (offset & (handle->page_size - 1)) {\n\t\terrno = -EINVAL;\n\t\ttracecmd_critical(\"bad page offset %llx\", offset);\n\t\treturn -1;\n\t}\n\n\tif (offset < handle->cpu_data[cpu].file_offset ||\n\t    offset > handle->cpu_data[cpu].file_offset +\n\t    handle->cpu_data[cpu].file_size) {\n\t\terrno = -EINVAL;\n\t\ttracecmd_critical(\"bad page offset %llx\", offset);\n\t\treturn -1;\n\t}\n\n\thandle->cpu_data[cpu].offset = offset;\n\thandle->cpu_data[cpu].size = (handle->cpu_data[cpu].file_offset +\n\t\t\t\t      handle->cpu_data[cpu].file_size) -\n\t\t\t\t\toffset;\n\n\tfree_page(handle, cpu);\n\n\thandle->cpu_data[cpu].page = allocate_page(handle, cpu, offset);\n\tif (!handle->cpu_data[cpu].page)\n\t\treturn -1;\n\n\tif (update_page_info(handle, cpu))\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic int get_next_page(struct tracecmd_input *handle, int cpu)\n{\n\toff_t offset;\n\n\tif (!handle->cpu_data[cpu].page && !handle->use_pipe)\n\t\treturn 0;\n\n\tfree_page(handle, cpu);\n\n\tif (handle->cpu_data[cpu].size <= handle->page_size) {\n\t\thandle->cpu_data[cpu].offset = 0;\n\t\treturn 0;\n\t}\n\n\toffset = handle->cpu_data[cpu].offset + handle->page_size;\n\n\treturn get_page(handle, cpu, offset);\n}\n\nstatic struct tep_record *\npeek_event(struct tracecmd_input *handle, unsigned long long offset,\n\t   int cpu)\n{\n\tstruct tep_record *record = NULL;\n\n\t/*\n\t * Since the timestamp is calculated from the beginning\n\t * of the page and through each event, we reset the\n\t * page to the beginning. This is just used by\n\t * tracecmd_read_at.\n\t */\n\tupdate_page_info(handle, cpu);\n\n\tdo {\n\t\tfree_next(handle, cpu);\n\t\trecord = tracecmd_peek_data(handle, cpu);\n\t\tif (record && (record->offset + record->record_size) > offset)\n\t\t\tbreak;\n        } while (record);\n\n\treturn record;\n}\n\nstatic struct tep_record *\nread_event(struct tracecmd_input *handle, unsigned long long offset,\n\t   int cpu)\n{\n\tstruct tep_record *record;\n\n\trecord = peek_event(handle, offset, cpu);\n\tif (record)\n\t\trecord = tracecmd_read_data(handle, cpu);\n\treturn record;\n}\n\nstatic struct tep_record *\nfind_and_peek_event(struct tracecmd_input *handle, unsigned long long offset,\n\t\t    int *pcpu)\n{\n\tunsigned long long page_offset;\n\tint cpu;\n\n\t/* find the cpu that this offset exists in */\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\tif (offset >= handle->cpu_data[cpu].file_offset &&\n\t\t    offset < handle->cpu_data[cpu].file_offset +\n\t\t    handle->cpu_data[cpu].file_size)\n\t\t\tbreak;\n\t}\n\n\t/* Not found? */\n\tif (cpu == handle->cpus)\n\t\treturn NULL;\n\n\t/* Move this cpu index to point to this offest */\n\tpage_offset = calc_page_offset(handle, offset);\n\n\tif (get_page(handle, cpu, page_offset) < 0)\n\t\treturn NULL;\n\n\tif (pcpu)\n\t\t*pcpu = cpu;\n\n\treturn peek_event(handle, offset, cpu);\n}\n\n\nstatic struct tep_record *\nfind_and_read_event(struct tracecmd_input *handle, unsigned long long offset,\n\t\t    int *pcpu)\n{\n\tstruct tep_record *record;\n\tint cpu;\n\n\trecord = find_and_peek_event(handle, offset, &cpu);\n\tif (record) {\n\t\trecord = tracecmd_read_data(handle, cpu);\n\t\tif (pcpu)\n\t\t\t*pcpu = cpu;\n\t}\n\treturn record;\n}\n\n/**\n * tracecmd_read_at - read a record from a specific offset\n * @handle: input handle for the trace.dat file\n * @offset: the offset into the file to find the record\n * @pcpu: pointer to a variable to store the CPU id the record was found in\n *\n * This function is useful when looking for a previous record.\n * You can store the offset of the record \"record->offset\" and use that\n * offset to retreive the record again without needing to store any\n * other information about the record.\n *\n * The record returned must be freed.\n */\nstruct tep_record *\ntracecmd_read_at(struct tracecmd_input *handle, unsigned long long offset,\n\t\t int *pcpu)\n{\n\tunsigned long long page_offset;\n\tint cpu;\n\n\tpage_offset = calc_page_offset(handle, offset);\n\n\t/* check to see if we have this page already */\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\tif (handle->cpu_data[cpu].offset == page_offset &&\n\t\t    handle->cpu_data[cpu].file_size)\n\t\t\tbreak;\n\t}\n\n\tif (cpu < handle->cpus && handle->cpu_data[cpu].page) {\n\t\tif (pcpu)\n\t\t\t*pcpu = cpu;\n\t\treturn read_event(handle, offset, cpu);\n\t} else\n\t\treturn find_and_read_event(handle, offset, pcpu);\n}\n\n/**\n * tracecmd_refresh_record - remaps the records data\n * @handle: input handle for the trace.dat file\n * @record: the record to be refreshed\n *\n * A record data points to a mmap section of memory.\n * by reading new records the mmap section may be unmapped.\n * This will refresh the record's data mapping.\n *\n * ===== OBSOLETED BY PAGE REFERENCES =====\n *\n * Returns 1 if page is still mapped (does not modify CPU iterator)\n *         0 on successful mapping (was not mapped before,\n *                      This will update CPU iterator to point to\n *                      the next record)\n *        -1 on error.\n */\nint tracecmd_refresh_record(struct tracecmd_input *handle,\n\t\t\t    struct tep_record *record)\n{\n\tunsigned long long page_offset;\n\tint cpu = record->cpu;\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\tint index;\n\tint ret;\n\n\tpage_offset = calc_page_offset(handle, record->offset);\n\tindex = record->offset & (handle->page_size - 1);\n\n\tret = get_page(handle, record->cpu, page_offset);\n\tif (ret < 0)\n\t\treturn -1;\n\n\t/* If the page is still mapped, there's nothing to do */\n\tif (ret)\n\t\treturn 1;\n\n\trecord->data = kbuffer_read_at_offset(cpu_data->kbuf, index, &record->ts);\n\tcpu_data->timestamp = record->ts;\n\n\treturn 0;\n}\n\n/**\n * tracecmd_read_cpu_first - get the first record in a CPU\n * @handle: input handle for the trace.dat file\n * @cpu: the CPU to search\n *\n * This returns the first (by time) record entry in a given CPU.\n *\n * The record returned must be freed.\n */\nstruct tep_record *\ntracecmd_read_cpu_first(struct tracecmd_input *handle, int cpu)\n{\n\tunsigned long long page_offset;\n\tint ret;\n\n\tif (cpu >= handle->cpus)\n\t\treturn NULL;\n\n\tpage_offset = calc_page_offset(handle, handle->cpu_data[cpu].file_offset);\n\n\tret = get_page(handle, cpu, page_offset);\n\tif (ret < 0)\n\t\treturn NULL;\n\n\t/* If the page was already mapped, we need to reset it */\n\tif (ret)\n\t\tupdate_page_info(handle, cpu);\n\n\tfree_next(handle, cpu);\n\n\treturn tracecmd_read_data(handle, cpu);\n}\n\n/**\n * tracecmd_iterate_reset - Set the handle to iterate from the beginning\n * @handle: input handle for the trace.dat file\n *\n * This causes tracecmd_iterate_events*() to start from the beginning\n * of the trace.dat file.\n */\nint tracecmd_iterate_reset(struct tracecmd_input *handle)\n{\n\tunsigned long long page_offset;\n\tint cpu;\n\tint ret = 0;\n\tint r;\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\tpage_offset = calc_page_offset(handle, handle->cpu_data[cpu].file_offset);\n\n\t\tr = get_page(handle, cpu, page_offset);\n\t\tif (r < 0) {\n\t\t\tret = -1;\n\t\t\tcontinue; /* ?? */\n\t\t}\n\n\t\t/* If the page was already mapped, we need to reset it */\n\t\tif (r)\n\t\t\tupdate_page_info(handle, cpu);\n\n\t\tfree_next(handle, cpu);\n\t}\n\treturn ret;\n}\n\n/**\n * tracecmd_read_cpu_last - get the last record in a CPU\n * @handle: input handle for the trace.dat file\n * @cpu: the CPU to search\n *\n * This returns the last (by time) record entry in a given CPU.\n *\n * The record returned must be freed.\n */\nstruct tep_record *\ntracecmd_read_cpu_last(struct tracecmd_input *handle, int cpu)\n{\n\tstruct tep_record *record = NULL;\n\toff_t offset, page_offset;\n\n\toffset = handle->cpu_data[cpu].file_offset +\n\t\thandle->cpu_data[cpu].file_size;\n\n\tif (offset & (handle->page_size - 1))\n\t\toffset &= ~(handle->page_size - 1);\n\telse\n\t\toffset -= handle->page_size;\n\n\tpage_offset = offset;\n\n again:\n\tif (get_page(handle, cpu, page_offset) < 0)\n\t\treturn NULL;\n\n\toffset = page_offset;\n\n\tdo {\n\t\ttracecmd_free_record(record);\n\t\trecord = tracecmd_read_data(handle, cpu);\n\t\tif (record)\n\t\t\toffset = record->offset;\n\t} while (record);\n\n\trecord = tracecmd_read_at(handle, offset, NULL);\n\n\t/*\n\t * It is possible that a page has just a timestamp\n\t * or just padding on it.\n\t */\n\tif (!record) {\n\t\tif (page_offset == handle->cpu_data[cpu].file_offset)\n\t\t\treturn NULL;\n\t\tpage_offset -= handle->page_size;\n\t\tgoto again;\n\t}\n\n\treturn record;\n}\n\n/**\n * tracecmd_set_cpu_to_timestamp - set the CPU iterator to a given time\n * @handle: input handle for the trace.dat file\n * @cpu: the CPU pointer to set\n * @ts: the timestamp to set the CPU at.\n *\n * This sets the CPU iterator used by tracecmd_read_data and\n * tracecmd_peek_data to a location in the CPU storage near\n * a given timestamp. It will try to set the iterator to a time before\n * the time stamp and not actually at a given time.\n *\n * To use this to find a record in a time field, call this function\n * first, than iterate with tracecmd_read_data to find the records\n * you need.\n */\nint\ntracecmd_set_cpu_to_timestamp(struct tracecmd_input *handle, int cpu,\n\t\t\t      unsigned long long ts)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\toff_t start, end, next;\n\n\tif (cpu < 0 || cpu >= handle->cpus) {\n\t\terrno = -EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (!cpu_data->size)\n\t\treturn -1;\n\n\tif (!cpu_data->page) {\n\t\tif (init_cpu(handle, cpu))\n\t\t    return -1;\n\t}\n\n\tif (cpu_data->timestamp == ts) {\n\t\t/*\n\t\t * If a record is cached, then that record is most\n\t\t * likely the matching timestamp. Otherwise we need\n\t\t * to start from the beginning of the index;\n\t\t */\n\t\tif (!cpu_data->next ||\n\t\t    cpu_data->next->ts != ts)\n\t\t\tupdate_page_info(handle, cpu);\n\t\treturn 0;\n\t}\n\n\t/* Set to the first record on current page */\n\tupdate_page_info(handle, cpu);\n\n\tif (cpu_data->timestamp < ts) {\n\t\tstart = cpu_data->offset;\n\t\tend = cpu_data->file_offset + cpu_data->file_size;\n\t\tif (end & (handle->page_size - 1))\n\t\t\tend &= ~(handle->page_size - 1);\n\t\telse\n\t\t\tend -= handle->page_size;\n\t\tnext = end;\n\t} else {\n\t\tend = cpu_data->offset;\n\t\tstart = cpu_data->file_offset;\n\t\tnext = start;\n\t}\n\n\twhile (start < end) {\n\t\tif (get_page(handle, cpu, next) < 0)\n\t\t\treturn -1;\n\n\t\tif (cpu_data->timestamp == ts)\n\t\t\tbreak;\n\n\t\tif (cpu_data->timestamp < ts)\n\t\t\tstart = next;\n\t\telse\n\t\t\tend = next;\n\n\t\tnext = start + (end - start) / 2;\n\t\tnext = calc_page_offset(handle, next);\n\n\t\t/* Prevent an infinite loop if start and end are a page off */\n\t\tif (next == start)\n\t\t\tstart = next += handle->page_size;\n\t}\n\n\t/*\n\t * We need to end up on a page before the time stamp.\n\t * We go back even if the timestamp is the same. This is because\n\t * we want the event with the timestamp, not the page. The page\n\t * can start with the timestamp we are looking for, but the event\n\t * may be on the previous page.\n\t */\n\tif (cpu_data->timestamp >= ts &&\n\t    cpu_data->offset > cpu_data->file_offset)\n\t\tget_page(handle, cpu, cpu_data->offset - handle->page_size);\n\n\treturn 0;\n}\n\n/**\n * tracecmd_set_all_cpus_to_timestamp - set all CPUs iterator to a given time\n * @handle: input handle for the trace.dat file\n * @cpu: the CPU pointer to set\n * @ts: the timestamp to set the CPU at.\n *\n * This sets the CPU iterator used by tracecmd_read_data and\n * tracecmd_peek_data to a location in the CPU storage near\n * a given timestamp. It will try to set the iterator to a time before\n * the time stamp and not actually at a given time.\n *\n * To use this to find a record in a time field, call this function\n * first, than iterate with tracecmd_read_next_data to find the records\n * you need.\n */\nvoid\ntracecmd_set_all_cpus_to_timestamp(struct tracecmd_input *handle,\n\t\t\t\t   unsigned long long time)\n{\n\tint cpu;\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++)\n\t\ttracecmd_set_cpu_to_timestamp(handle, cpu, time);\n}\n\n/**\n * tracecmd_set_cursor - set the offset for the next tracecmd_read_data\n * @handle: input handle for the trace.dat file\n * @cpu: the CPU pointer to set\n * @offset: the offset to place the cursor\n *\n * Set the pointer to the next read or peek. This is useful when\n * needing to read sequentially and then look at another record\n * out of sequence without breaking the iteration. This is done with:\n *\n *  record = tracecmd_peek_data()\n *  offset = record->offset;\n *  record = tracecmd_read_at();\n *   - do what ever with record -\n *  tracecmd_set_cursor(handle, cpu, offset);\n *\n *  Now the next tracecmd_peek_data or tracecmd_read_data will return\n *  the original record.\n */\nint tracecmd_set_cursor(struct tracecmd_input *handle, int cpu, size_t offset)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\tunsigned long long page_offset;\n\n\tif (cpu < 0 || cpu >= handle->cpus)\n\t\treturn -1;\n\n\tif (offset < cpu_data->file_offset ||\n\t    offset > cpu_data->file_offset + cpu_data->file_size)\n\t\treturn -1; \t/* cpu does not have this offset. */\n\n\t/* Move this cpu index to point to this offest */\n\tpage_offset = calc_page_offset(handle, offset);\n\n\tif (get_page(handle, cpu, page_offset) < 0)\n\t\treturn -1;\n\n\tpeek_event(handle, offset, cpu);\n\n\treturn 0;\n}\n\n/**\n * tracecmd_get_cursor - get the offset for the next tracecmd_read_data\n * @handle: input handle for the trace.dat file\n * @cpu: the CPU pointer to get the cursor from\n *\n * Returns the offset of the next record that would be read.\n */\nunsigned long long\ntracecmd_get_cursor(struct tracecmd_input *handle, int cpu)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\tstruct kbuffer *kbuf = cpu_data->kbuf;\n\n\tif (cpu < 0 || cpu >= handle->cpus)\n\t\treturn 0;\n\n\t/*\n\t * Use the next pointer if it exists and matches the\n\t * current timestamp.\n\t */\n\tif (cpu_data->next &&\n\t    cpu_data->next->ts == cpu_data->timestamp)\n\t\treturn cpu_data->next->offset;\n\n\t/*\n\t * Either the next point does not exist, or it does\n\t * not match the timestamp. The next read will use the\n\t * current page.\n\t *\n\t * If the offset is at the end, then return that.\n\t */\n\tif (cpu_data->offset >= cpu_data->file_offset +\n\t    cpu_data->file_size)\n\t\treturn cpu_data->offset;\n\n\treturn cpu_data->offset + kbuffer_curr_offset(kbuf);\n}\n\n/**\n * tracecmd_translate_data - create a record from raw data\n * @handle: input handle for the trace.dat file\n * @ptr: raw data to read\n * @size: the size of the data\n *\n * This function tries to create a record from some given\n * raw data. The data does not need to be from the trace.dat file.\n * It can be stored from another location.\n *\n * Note, since the timestamp is calculated from within the trace\n * buffer, the timestamp for the record will be zero, since it\n * can't calculate it.\n *\n * The record returned must be freed.\n */\nstruct tep_record *\ntracecmd_translate_data(struct tracecmd_input *handle,\n\t\t\tvoid *ptr, int size)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tstruct tep_record *record;\n\tunsigned int length;\n\tint swap = 1;\n\n\t/* minimum record read is 8, (warn?) (TODO: make 8 into macro) */\n\tif (size < 8)\n\t\treturn NULL;\n\n\trecord = malloc(sizeof(*record));\n\tif (!record)\n\t\treturn NULL;\n\tmemset(record, 0, sizeof(*record));\n\n\trecord->ref_count = 1;\n\tif (tep_is_local_bigendian(pevent) == tep_is_file_bigendian(pevent))\n\t\tswap = 0;\n\trecord->data = kbuffer_translate_data(swap, ptr, &length);\n\trecord->size = length;\n\tif (record->data)\n\t\trecord->record_size = record->size + (record->data - ptr);\n\n\treturn record;\n}\n\n\n/**\n * tracecmd_peek_data - return the record at the current location.\n * @handle: input handle for the trace.dat file\n * @cpu: the CPU to pull from\n *\n * This returns the record at the current location of the CPU\n * iterator. It does not increment the CPU iterator.\n */\nstruct tep_record *\ntracecmd_peek_data(struct tracecmd_input *handle, int cpu)\n{\n\tstruct tep_record *record;\n\tunsigned long long ts;\n\tstruct kbuffer *kbuf;\n\tstruct page *page;\n\tint index;\n\tvoid *data;\n\n\tif (cpu >= handle->cpus)\n\t\treturn NULL;\n\n\tpage = handle->cpu_data[cpu].page;\n\tkbuf = handle->cpu_data[cpu].kbuf;\n\n\t/* Hack to work around function graph read ahead */\n\ttracecmd_curr_thread_handle = handle;\n\n\tif (handle->cpu_data[cpu].next) {\n\n\t\trecord = handle->cpu_data[cpu].next;\n\t\tif (!record->data) {\n\t\t\ttracecmd_critical(\"Something freed the record\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (handle->cpu_data[cpu].timestamp == record->ts)\n\t\t\treturn record;\n\n\t\t/*\n\t\t * The timestamp changed, which means the cached\n\t\t * record is no longer valid. Reread a new record.\n\t\t */\n\t\tfree_next(handle, cpu);\n\t}\n\nread_again:\n\tif (!page) {\n\t\tif (handle->use_pipe) {\n\t\t\tget_next_page(handle, cpu);\n\t\t\tpage = handle->cpu_data[cpu].page;\n\t\t}\n\t\tif (!page)\n\t\t\treturn NULL;\n\t}\n\n\tdata = kbuffer_read_event(kbuf, &ts);\n\tif (!data) {\n\t\tif (get_next_page(handle, cpu))\n\t\t\treturn NULL;\n\t\tpage = handle->cpu_data[cpu].page;\n\t\tgoto read_again;\n\t}\n\n\thandle->cpu_data[cpu].timestamp = timestamp_calc(ts, cpu, handle);\n\n\tindex = kbuffer_curr_offset(kbuf);\n\n\trecord = malloc(sizeof(*record));\n\tif (!record)\n\t\treturn NULL;\n\tmemset(record, 0, sizeof(*record));\n\n\trecord->ts = handle->cpu_data[cpu].timestamp;\n\trecord->size = kbuffer_event_size(kbuf);\n\trecord->cpu = handle->cpu_data[cpu].cpu;\n\trecord->data = data;\n\trecord->offset = handle->cpu_data[cpu].offset + index;\n\trecord->missed_events = kbuffer_missed_events(kbuf);\n\trecord->ref_count = 1;\n\trecord->locked = 1;\n\n\thandle->cpu_data[cpu].next = record;\n\n\trecord->record_size = kbuffer_curr_size(kbuf);\n\trecord->priv = page;\n\tadd_record(page, record);\n\tpage->ref_count++;\n\n\tkbuffer_next_event(kbuf, NULL);\n\n\treturn record;\n}\n\n/**\n * tracecmd_read_data - read the next record and increment\n * @handle: input handle for the trace.dat file\n * @cpu: the CPU to pull from\n *\n * This returns the record at the current location of the CPU\n * iterator and increments the CPU iterator.\n *\n * The record returned must be freed.\n */\nstruct tep_record *\ntracecmd_read_data(struct tracecmd_input *handle, int cpu)\n{\n\tstruct tep_record *record;\n\n\tif (cpu >= handle->cpus)\n\t\treturn NULL;\n\n\trecord = tracecmd_peek_data(handle, cpu);\n\thandle->cpu_data[cpu].next = NULL;\n\tif (record) {\n\t\trecord->locked = 0;\n#if DEBUG_RECORD\n\t\trecord->alloc_addr = (unsigned long)__builtin_return_address(0);\n#endif\n\t}\n\treturn record;\n}\n\n/**\n * tracecmd_read_next_data - read the next record\n * @handle: input handle to the trace.dat file\n * @rec_cpu: return pointer to the CPU that the record belongs to\n *\n * This returns the next record by time. This is different than\n * tracecmd_read_data in that it looks at all CPUs. It does a peek\n * at each CPU and the record with the earliest time stame is\n * returned. If @rec_cpu is not NULL it gets the CPU id the record was\n * on. The CPU cursor of the returned record is moved to the\n * next record.\n *\n * Multiple reads of this function will return a serialized list\n * of all records for all CPUs in order of time stamp.\n *\n * The record returned must be freed.\n */\nstruct tep_record *\ntracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu)\n{\n\tstruct tep_record *record;\n\tint next_cpu;\n\n\trecord = tracecmd_peek_next_data(handle, &next_cpu);\n\tif (!record)\n\t\treturn NULL;\n\n\tif (rec_cpu)\n\t\t*rec_cpu = next_cpu;\n\n\treturn tracecmd_read_data(handle, next_cpu);\n}\n\n/**\n * tracecmd_follow_event - Add callback for specific events for iterators\n * @handle: The handle to get a callback from\n * @system: The system of the event to track\n * @event_name: The name of the event to track\n * @callback: The function to call when the event is hit in an iterator\n * @callback_data: The data to pass to @callback\n *\n * This attaches a callback to @handle where if tracecmd_iterate_events()\n * or tracecmd_iterate_events_multi() is called, that if the specified\n * event is hit, it will call @callback, with the following parameters:\n *  @handle: Same handle as passed to this function.\n *  @event: The event pointer that was found by @system and @event_name.\n *  @record; The event instance of @event.\n *  @cpu: The cpu that the event happened on.\n *  @callback_data: The same as @callback_data passed to the function.\n *\n * Note that when used with tracecmd_iterate_events_multi() that @cpu\n * may be the nth CPU of all handles it is processing, so if the CPU\n * that the @record is on is desired, then use @record->cpu.\n *\n * Returns 0 on success and -1 on error.\n */\nint tracecmd_follow_event(struct tracecmd_input *handle,\n\t\t\t  const char *system, const char *event_name,\n\t\t\t  int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t  struct tep_event *,\n\t\t\t\t\t  struct tep_record *,\n\t\t\t\t\t  int, void *),\n\t\t\t  void *callback_data)\n{\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tstruct follow_event *followers;\n\tstruct follow_event follow;\n\n\tif (!tep) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tfollow.event = tep_find_event_by_name(tep, system, event_name);\n\tif (!follow.event) {\n\t\terrno = ENOENT;\n\t\treturn -1;\n\t}\n\n\tfollow.callback = callback;\n\tfollow.callback_data = callback_data;\n\n\tfollowers = realloc(handle->followers, sizeof(*followers) *\n\t\t\t    (handle->nr_followers + 1));\n\tif (!followers)\n\t\treturn -1;\n\n\thandle->followers = followers;\n\tfollowers[handle->nr_followers++] = follow;\n\n\treturn 0;\n}\n\n/**\n * tracecmd_follow_missed_events - Add callback for missed events for iterators\n * @handle: The handle to get a callback from\n * @callback: The function to call when missed events is detected\n * @callback_data: The data to pass to @callback\n *\n * This attaches a callback to @handle where if tracecmd_iterate_events()\n * or tracecmd_iterate_events_multi() is called, that if missed events\n * is detected, it will call @callback, with the following parameters:\n *  @handle: Same handle as passed to this function.\n *  @event: The event pointer of the record with the missing events\n *  @record; The event instance of @event.\n *  @cpu: The cpu that the event happened on.\n *  @callback_data: The same as @callback_data passed to the function.\n *\n * Note that when used with tracecmd_iterate_events_multi() that @cpu\n * may be the nth CPU of all handles it is processing, so if the CPU\n * that the @record is on is desired, then use @record->cpu.\n *\n * If the count of missing events is available, @record->missed_events\n * will have a positive number holding the number of missed events since\n * the last event on the same CPU, or just -1 if that number is unknown\n * but missed events did happen.\n *\n * Returns 0 on success and -1 on error.\n */\nint tracecmd_follow_missed_events(struct tracecmd_input *handle,\n\t\t\t\t  int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t\t  struct tep_event *,\n\t\t\t\t\t\t  struct tep_record *,\n\t\t\t\t\t\t  int, void *),\n\t\t\t\t  void *callback_data)\n{\n\tstruct follow_event *followers;\n\tstruct follow_event follow;\n\n\tfollow.event = NULL;\n\tfollow.callback = callback;\n\tfollow.callback_data = callback_data;\n\n\tfollowers = realloc(handle->missed_followers, sizeof(*followers) *\n\t\t\t    (handle->nr_missed_followers + 1));\n\tif (!followers)\n\t\treturn -1;\n\n\thandle->missed_followers = followers;\n\tfollowers[handle->nr_missed_followers++] = follow;\n\n\treturn 0;\n}\n\nstatic int call_followers(struct tracecmd_input *handle,\n\t\t\t  struct tep_record *record, int cpu)\n{\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tstruct follow_event *followers = handle->followers;\n\tstruct tep_event *event;\n\tint ret = 0;\n\tint i;\n\n\tevent = tep_find_event_by_record(tep, record);\n\tif (!event)\n\t\treturn -1;\n\n\tfor (i = 0; i < handle->nr_followers; i++) {\n\t\tif (handle->followers[i].event == event)\n\t\t\tret |= followers[i].callback(handle, event, record,\n\t\t\t\t\t\t     cpu, followers[i].callback_data);\n\t}\n\n\treturn ret;\n}\n\nstatic int call_missed_events(struct tracecmd_input *handle,\n\t\t\t      struct tep_record *record, int cpu)\n{\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tstruct follow_event *followers = handle->missed_followers;\n\tstruct tep_event *event;\n\tint ret = 0;\n\tint i;\n\n\tevent = tep_find_event_by_record(tep, record);\n\tif (!event)\n\t\treturn -1;\n\n\tfor (i = 0; i < handle->nr_missed_followers; i++) {\n\t\tret |= followers[i].callback(handle, event, record,\n\t\t\t\t\t     cpu, followers[i].callback_data);\n\t}\n\n\treturn ret;\n}\n\nstatic int call_callbacks(struct tracecmd_input *handle, struct tep_record *record,\n\t\t\t  int next_cpu,\n\t\t\t  int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t  struct tep_record *,\n\t\t\t\t\t  int, void *),\n\t\t\t  void *callback_data)\n{\n\tint ret = 0;\n\n\tif (!record)\n\t\treturn 0;\n\n\tif (record->missed_events)\n\t\tret = call_missed_events(handle, record, next_cpu);\n\n\tif (ret)\n\t\treturn ret;\n\n\tif (!handle->filter ||\n\t    tcmd_filter_match(handle->filter, record) == TRACECMD_FILTER_MATCH) {\n\t\tif (handle->nr_followers)\n\t\t\tret = call_followers(handle, record, next_cpu);\n\t\tif (!ret && callback)\n\t\t\tret = callback(handle, record, next_cpu, callback_data);\n\t}\n\n\treturn ret;\n}\n\n/**\n * tracecmd_iterate_events - iterate events over a given handle\n * @handle: The handle to iterate over\n * @cpus: The CPU set to filter on (NULL for all CPUs)\n * @cpu_size: The size of @cpus (ignored if @cpus is NULL)\n * @callback: The callback function for each event\n * @callback_data: The data to pass to the @callback.\n *\n * Will loop over all events in @handle (filtered by the given @cpus),\n * and will call @callback for each event in order of the event's records\n * timestamp.\n *\n * Returns the -1 on error, or the value of the callbacks.\n */\nint tracecmd_iterate_events(struct tracecmd_input *handle,\n\t\t\t    cpu_set_t *cpus, int cpu_size,\n\t\t\t    int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t    struct tep_record *,\n\t\t\t\t\t    int, void *),\n\t\t\t    void *callback_data)\n{\n\tstruct tep_record *record;\n\tunsigned long long *timestamps;\n\tunsigned long long ts, last_timestamp = 0;\n\tint *cpu_list;\n\tint cpu_count = 0;\n\tint next_cpu;\n\tint cpu;\n\tint ret = 0;\n\tint i;\n\n\tif (!callback && !handle->nr_followers) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\ttimestamps = calloc(handle->cpus, sizeof(*timestamps));\n\tif (!timestamps)\n\t\treturn -1;\n\n\tcpu_list = calloc(handle->cpus, sizeof(*cpu_list));\n\tif (!cpu_list) {\n\t\tfree(timestamps);\n\t\treturn -1;\n\t}\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\tif (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))\n\t\t\tcontinue;\n\t\tcpu_list[cpu_count++] = cpu;\n\t}\n\n\tfor (i = 0; i < cpu_count; i++) {\n\t\tcpu = cpu_list[i];\n\t\trecord = tracecmd_peek_data(handle, cpu);\n\t\ttimestamps[cpu] = record ? record->ts : -1ULL;\n\t}\n\n\tdo {\n\t\tnext_cpu = -1;\n\t\tfor (i = 0; i < cpu_count; i++) {\n\t\t\tcpu = cpu_list[i];\n\t\t\tts = timestamps[cpu];\n\t\t\tif (ts == -1ULL)\n\t\t\t\tcontinue;\n\n\t\t\tif (next_cpu < 0 || ts < last_timestamp) {\n\t\t\t\tnext_cpu = cpu;\n\t\t\t\tlast_timestamp = ts;\n\t\t\t}\n\t\t}\n\t\tif (next_cpu >= 0) {\n\t\t\trecord = tracecmd_peek_data(handle, next_cpu);\n\n\t\t\t/* Make sure the record is still what we expect it to be */\n\t\t\tif (!record || record->ts != last_timestamp) {\n\t\t\t\ttimestamps[next_cpu] = record ? record->ts : -1ULL;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* Need to call read_data to increment to the next record */\n\t\t\trecord = tracecmd_read_data(handle, next_cpu);\n\n\t\t\tret = call_callbacks(handle, record, next_cpu,\n\t\t\t\t\t     callback, callback_data);\n\n\t\t\ttracecmd_free_record(record);\n\n\t\t\trecord = tracecmd_peek_data(handle, next_cpu);\n\t\t\ttimestamps[next_cpu] = record ? record->ts : -1ULL;\n\t\t}\n\t} while (next_cpu >= 0 && ret == 0);\n\n\tfree(timestamps);\n\tfree(cpu_list);\n\n\treturn ret;\n}\n\nstatic struct tep_record *\nload_records(struct tracecmd_input *handle, int cpu,\n\t     unsigned long long page_offset, unsigned long long start_offset)\n{\n\tstruct tep_record *last_record = NULL;\n\tstruct tep_record *record;\n\tunsigned long long page_end = page_offset + handle->page_size;\n\n\tif (get_page(handle, cpu, page_offset) < 0)\n\t\treturn NULL;\n\n\tupdate_page_info(handle, cpu);\n\n\tif (start_offset)\n\t\tpage_end = start_offset + 1;\n\n\tfor (;;) {\n\t\trecord = tracecmd_read_data(handle, cpu);\n\t\tif (!record || record->offset >= page_end) {\n\t\t\t/* Make sure the cpu_data page is still valid */\n\t\t\tget_page(handle, cpu, page_offset);\n\t\t\ttracecmd_free_record(record);\n\t\t\tbreak;\n\t\t}\n\t\t/*\n\t\t * Hijack the record->priv, as we know that it points\n\t\t * to handle->cpu_data[cpu].page, and use that as\n\t\t * a link list of all the records on this page going\n\t\t * backwards.\n\t\t */\n\t\trecord->priv = last_record;\n\t\tlast_record = record;\n\t}\n\n\treturn last_record;\n}\n\nstatic void initialize_last_events(struct tracecmd_input *handle,\n\t\t\t\t   struct tep_record **last_records,\n\t\t\t\t   cpu_set_t *cpu_set, int cpu_size,\n\t\t\t\t   int cpus, bool cont)\n{\n\tunsigned long long page_offset;\n\tunsigned long long start_offset = 0;\n\tstruct tep_record *record;\n\tint cpu;\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tif (cpu_set && !CPU_ISSET_S(cpu, cpu_size, cpu_set))\n\t\t\tcontinue;\n\n\t\tif (!handle->cpu_data[cpu].file_size)\n\t\t\tcontinue;\n\n\t\tif (cont) {\n\t\t\trecord = tracecmd_read_data(handle, cpu);\n\t\t\tif (record)\n\t\t\t\tpage_offset = start_offset = record->offset;\n\t\t\ttracecmd_free_record(record);\n\t\t}\n\n\t\tif (!start_offset) {\n\t\t\t/* Find the start of the last page for this CPU */\n\t\t\tpage_offset = handle->cpu_data[cpu].file_offset +\n\t\t\t\thandle->cpu_data[cpu].file_size;\n\t\t}\n\t\tpage_offset = calc_page_offset(handle, page_offset - 1);\n\n\t\tlast_records[cpu] = load_records(handle, cpu, page_offset, start_offset);\n\t}\n}\n\nstatic struct tep_record *peek_last_event(struct tracecmd_input *handle,\n\t\t\t\t\t  struct tep_record **last_records, int cpu)\n{\n\tstruct tep_record *record = last_records[cpu];\n\tstruct page *page = handle->cpu_data[cpu].page;\n\tunsigned long long page_offset;\n\n\tif (record)\n\t\treturn record;\n\n\t/* page can be NULL if the size is zero */\n\tif (!page)\n\t\treturn NULL;\n\n\tpage_offset = page->offset - handle->page_size;\n\tif (page_offset < handle->cpu_data[cpu].file_offset)\n\t\treturn NULL;\n\n\tlast_records[cpu] = load_records(handle, cpu, page_offset, 0);\n\treturn peek_last_event(handle, last_records, cpu);\n}\n\nstatic struct tep_record *next_last_event(struct tracecmd_input *handle,\n\t\t\t\t\t  struct tep_record **last_records, int cpu)\n{\n\tstruct tep_record *record = last_records[cpu];\n\tstruct page *page = handle->cpu_data[cpu].page;\n\n\tif (!record)\n\t\treturn NULL;\n\n\tlast_records[cpu] = record->priv;\n\trecord->priv = page;\n\n\treturn record;\n}\n\nstatic void free_last_events(struct tracecmd_input *handle,\n\t\t\t     struct tep_record **last_records,\n\t\t\t     cpu_set_t *cpu_set, int cpu_size,\n\t\t\t     int cpus)\n{\n\tstruct tep_record *record;\n\tint cpu;\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tif (cpus && !CPU_ISSET_S(cpu, cpu_size, cpu_set))\n\t\t\tcontinue;\n\n\t\tdo {\n\t\t\trecord = next_last_event(handle, last_records, cpu);\n\t\t\ttracecmd_free_record(record);\n\t\t} while (record);\n\t}\n}\n\n/**\n * tracecmd_iterate_events_reverse - iterate events over a given handle backwards\n * @handle: The handle to iterate over\n * @cpus: The CPU set to filter on (NULL for all CPUs)\n * @cpu_size: The size of @cpus (ignored if @cpus is NULL)\n * @callback: The callback function for each event\n * @callback_data: The data to pass to the @callback.\n * @cont: If true, start where it left off, otherwise start at the end.\n *\n * Will loop over all events in @handle (filtered by the given @cpus),\n * and will call @callback for each event in reverse order.\n *\n * Returns the -1 on error, or the value of the callbacks.\n */\nint tracecmd_iterate_events_reverse(struct tracecmd_input *handle,\n\t\t\t\t    cpu_set_t *cpus, int cpu_size,\n\t\t\t\t    int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t\t    struct tep_record *,\n\t\t\t\t\t\t    int, void *),\n\t\t\t\t    void *callback_data, bool cont)\n{\n\tunsigned long long last_timestamp = 0;\n\tunsigned long long page_offset = 0;\n\tstruct tep_record **records;\n\tstruct tep_record *record;\n\tint next_cpu;\n\tint max_cpus = handle->cpus;\n\tint cpu;\n\tint ret = 0;\n\n\tif (!callback && !handle->nr_followers) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\trecords = calloc(max_cpus, sizeof(*records));\n\tif (!records)\n\t\treturn -1;\n\n\tinitialize_last_events(handle, records, cpus, cpu_size, max_cpus, cont);\n\n\tdo {\n\t\tnext_cpu = -1;\n\t\tfor (cpu = 0; cpu < max_cpus; cpu++) {\n\t\t\tif (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))\n\t\t\t\tcontinue;\n\t\t\trecord = peek_last_event(handle, records, cpu);\n\t\t\tif (!record)\n\t\t\t\tcontinue;\n\n\t\t\tif (next_cpu < 0 || record->ts > last_timestamp) {\n\t\t\t\tnext_cpu = cpu;\n\t\t\t\tlast_timestamp = record->ts;\n\t\t\t}\n\t\t}\n\t\tif (next_cpu >= 0) {\n\t\t\trecord = next_last_event(handle, records, next_cpu);;\n\t\t\tret = call_callbacks(handle, record, next_cpu,\n\t\t\t\t\t     callback, callback_data);\n\t\t\tif (ret)\n\t\t\t\tpage_offset = record->offset;\n\t\t\ttracecmd_free_record(record);\n\t\t}\n\t} while (next_cpu >= 0 && ret == 0);\n\n\tfree_last_events(handle, records, cpus, cpu_size, max_cpus);\n\tfree(records);\n\n\t/*\n\t * If the callback exited out early, then set the cursor back\n\t * to the location of that record so that if this gets called\n\t * again with cont = true, it will continue where it left off.\n\t */\n\tif (page_offset) {\n\t\t/* Set the internal cursor to the last record that was read */\n\t\trecord = tracecmd_read_at(handle, page_offset, NULL);\n\t\ttracecmd_free_record(record);\n\t}\n\n\treturn ret;\n}\n\nstruct record_handle {\n\tunsigned long long\t\tts;\n\tstruct tracecmd_input\t\t*handle;\n};\n\n/**\n * tracecmd_iterate_events_multi - iterate events over multiple handles\n * @handles: An array of handles to iterate over\n * @nr_handles: The number of handles in the @handles array.\n * @callback: The callback function for each event\n * @callback_data: The data to pass to the @callback.\n *\n * Will loop over all CPUs for each handle in @handles and call the\n * @callback in the order of the timestamp for each event's record\n * for each handle.\n *\n * Returns the -1 on error, or the value of the callbacks.\n */\nint tracecmd_iterate_events_multi(struct tracecmd_input **handles,\n\t\t\t\t  int nr_handles,\n\t\t\t\t  int (*callback)(struct tracecmd_input *handle,\n\t\t\t\t\t\t  struct tep_record *,\n\t\t\t\t\t\t  int, void *),\n\t\t\t\t  void *callback_data)\n{\n\tstruct tracecmd_input *handle;\n\tstruct record_handle *records;\n\tstruct tep_record *record;\n\tunsigned long long ts, last_timestamp = 0;\n\tint next_cpu;\n\tint cpus = 0;\n\tint all_cpus = 0;\n\tint cpu;\n\tint i;\n\tint ret = 0;\n\n\tfor (i = 0; i < nr_handles; i++) {\n\t\thandle = handles[i];\n\t\tcpus += handle->cpus;\n\t}\n\n\trecords = calloc(cpus, sizeof(*records));\n\tif (!records)\n\t\treturn -1;\n\n\tfor (i = 0; i < nr_handles; i++) {\n\t\thandle = handles[i];\n\t\thandle->start_cpu = all_cpus;\n\t\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\t\trecord = tracecmd_peek_data(handle, cpu);\n\t\t\trecords[all_cpus + cpu].ts = record ? record->ts : -1ULL;\n\t\t\trecords[all_cpus + cpu].handle = handle;\n\t\t}\n\t\tall_cpus += cpu;\n\t}\n\n\tdo {\n\t\tnext_cpu = -1;\n\t\tfor (cpu = 0; cpu < all_cpus; cpu++) {\n\t\t\tts = records[cpu].ts;\n\t\t\tif (ts == -1ULL)\n\t\t\t\tcontinue;\n\n\t\t\tif (next_cpu < 0 || ts < last_timestamp) {\n\t\t\t\tnext_cpu = cpu;\n\t\t\t\tlast_timestamp = ts;\n\t\t\t}\n\t\t}\n\t\tif (next_cpu >= 0) {\n\t\t\thandle = records[next_cpu].handle;\n\t\t\tcpu = next_cpu - handle->start_cpu;\n\n\t\t\t/* Refresh record as callback could have changed */\n\t\t\trecord = tracecmd_peek_data(handle, cpu);\n\n\t\t\t/* If the record updated, try again */\n\t\t\tif (!record || record->ts != last_timestamp) {\n\t\t\t\trecords[next_cpu].ts = record ? record->ts : -1ULL;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* Need to call read_data to increment to the next record */\n\t\t\trecord = tracecmd_read_data(handle, cpu);\n\n\t\t\tret = call_callbacks(handle, record, next_cpu,\n\t\t\t\t\t     callback, callback_data);\n\n\t\t\ttracecmd_free_record(record);\n\t\t}\n\n\t} while (next_cpu >= 0 && ret == 0);\n\n\tfree(records);\n\n\treturn ret;\n}\n\n/**\n * tracecmd_peek_next_data - return the next record\n * @handle: input handle to the trace.dat file\n * @rec_cpu: return pointer to the CPU that the record belongs to\n *\n * This returns the next record by time. This is different than\n * tracecmd_peek_data in that it looks at all CPUs. It does a peek\n * at each CPU and the record with the earliest time stame is\n * returned. If @rec_cpu is not NULL it gets the CPU id the record was\n * on. It does not increment the CPU iterator.\n */\nstruct tep_record *\ntracecmd_peek_next_data(struct tracecmd_input *handle, int *rec_cpu)\n{\n\tunsigned long long ts;\n\tstruct tep_record *record, *next_record = NULL;\n\tint next_cpu;\n\tint cpu;\n\n\tif (rec_cpu)\n\t\t*rec_cpu = -1;\n\n\tnext_cpu = -1;\n\tts = 0;\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\trecord = tracecmd_peek_data(handle, cpu);\n\t\tif (record && (!next_record || record->ts < ts)) {\n\t\t\tts = record->ts;\n\t\t\tnext_cpu = cpu;\n\t\t\tnext_record = record;\n\t\t}\n\t}\n\n\tif (next_record) {\n\t\tif (rec_cpu)\n\t\t\t*rec_cpu = next_cpu;\n\t\treturn next_record;\n\t}\n\n\treturn NULL;\n}\n\n/**\n * tracecmd_read_prev - read the record before the given record\n * @handle: input handle to the trace.dat file\n * @record: the record to use to find the previous record.\n *\n * This returns the record before the @record on its CPU. If\n * @record is the first record, NULL is returned. The cursor is set\n * as if the previous record was read by tracecmd_read_data().\n *\n * @record can not be NULL, otherwise NULL is returned; the\n * record ownership goes to this function.\n *\n * Note, this is not that fast of an algorithm, since it needs\n * to build the timestamp for the record.\n *\n * The record returned must be freed with tracecmd_free_record().\n */\nstruct tep_record *\ntracecmd_read_prev(struct tracecmd_input *handle, struct tep_record *record)\n{\n\tunsigned long long offset, page_offset;;\n\tstruct cpu_data *cpu_data;\n\tint index;\n\tint cpu;\n\n\tif (!record)\n\t\treturn NULL;\n\n\tcpu = record->cpu;\n\toffset = record->offset;\n\tcpu_data = &handle->cpu_data[cpu];\n\n\tpage_offset = calc_page_offset(handle, offset);\n\tindex = offset - page_offset;\n\n\t/* Note, the record passed in could have been a peek */\n\tfree_next(handle, cpu);\n\n\t/* Reset the cursor */\n\t/* Should not happen */\n\tif (get_page(handle, cpu, page_offset) < 0)\n\t\treturn NULL;\n\n\tupdate_page_info(handle, cpu);\n\n\t/* Find the record before this record */\n\tindex = 0;\n\tfor (;;) {\n\t\trecord = tracecmd_read_data(handle, cpu);\n\t\t/* Should not happen! */\n\t\tif (!record)\n\t\t\treturn NULL;\n\t\tif (record->offset == offset)\n\t\t\tbreak;\n\t\tindex = record->offset - page_offset;\n\t\ttracecmd_free_record(record);\n\t}\n\ttracecmd_free_record(record);\n\n\tif (index)\n\t\t/* we found our record */\n\t\treturn tracecmd_read_at(handle, page_offset + index, NULL);\n\n\t/* reset the index to start at the beginning of the page */\n\tupdate_page_info(handle, cpu);\n\n\t/* The previous record is on the previous page */\n\tfor (;;) {\n\t\t/* check if this is the first page */\n\t\tif (page_offset == cpu_data->file_offset)\n\t\t\treturn NULL;\n\t\tpage_offset -= handle->page_size;\n\n\t\t/* Updating page to a new page will reset index to 0 */\n\t\tget_page(handle, cpu, page_offset);\n\n\t\trecord = NULL;\n\t\tindex = 0;\n\t\tdo {\n\t\t\tif (record) {\n\t\t\t\tindex = record->offset - page_offset;\n\t\t\t\ttracecmd_free_record(record);\n\t\t\t}\n\t\t\trecord = tracecmd_read_data(handle, cpu);\n\t\t\t/* Should not happen */\n\t\t\tif (!record)\n\t\t\t\treturn NULL;\n\t\t} while (record->offset != offset);\n\t\ttracecmd_free_record(record);\n\n\t\tif (index)\n\t\t\t/* we found our record */\n\t\t\treturn tracecmd_read_at(handle, page_offset + index, NULL);\n\t}\n\n\t/* Not reached */\n}\n\nstatic int init_cpu_zfile(struct tracecmd_input *handle, int cpu)\n{\n\tstruct cpu_data *cpu_data;\n\toff_t offset;\n\tsize_t size;\n\n\tcpu_data = &handle->cpu_data[cpu];\n\toffset = lseek(handle->fd, 0, SEEK_CUR);\n\tif (lseek(handle->fd, cpu_data->file_offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\tstrcpy(cpu_data->compress.file, COMPR_TEMP_FILE);\n\tcpu_data->compress.fd = mkstemp(cpu_data->compress.file);\n\tif (cpu_data->compress.fd < 0)\n\t\treturn -1;\n\n\tif (tracecmd_uncompress_copy_to(handle->compress, cpu_data->compress.fd, NULL, &size))\n\t\treturn -1;\n\n\tif (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\tcpu_data->file_offset = handle->next_offset;\n\thandle->next_offset = (handle->next_offset + size + handle->page_size - 1) &\n\t\t~(handle->page_size - 1);\n\tcpu_data->offset = cpu_data->file_offset;\n\n\tcpu_data->file_size = size;\n\tcpu_data->size = size;\n\treturn 0;\n}\n\nstatic int init_cpu_zpage(struct tracecmd_input *handle, int cpu)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\tint count;\n\tint i;\n\n\tif (lseek(handle->fd, cpu_data->file_offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\tcount = tracecmd_load_chunks_info(handle->compress, &cpu_data->compress.chunks);\n\tif (count < 0)\n\t\treturn -1;\n\n\tcpu_data->compress.count = count;\n\tcpu_data->compress.last_chunk = 0;\n\n\tcpu_data->file_offset = handle->next_offset;\n\tcpu_data->file_size = 0;\n\n\tfor (i = 0; i < count; i++)\n\t\tcpu_data->file_size += cpu_data->compress.chunks[i].size;\n\n\tcpu_data->offset = cpu_data->file_offset;\n\tcpu_data->size = cpu_data->file_size;\n\thandle->next_offset = (handle->next_offset + cpu_data->file_size + handle->page_size - 1) &\n\t\t~(handle->page_size - 1);\n\treturn 0;\n}\n\nstatic int compress_cmp(const struct trace_rbtree_node *A,\n\t\t\tconst struct trace_rbtree_node *B)\n{\n\tconst struct zchunk_cache *cacheA;\n\tconst struct zchunk_cache *cacheB;\n\n\tcacheA = container_of(A, struct zchunk_cache, node);\n\tcacheB = container_of(B, struct zchunk_cache, node);\n\n\treturn chunk_cmp(cacheA->chunk, cacheB->chunk);\n}\n\nstatic int compress_search(const struct trace_rbtree_node *A,\n\t\t\t   const void *data)\n{\n\tconst struct zchunk_cache *cache;\n\toff_t offset = *(off_t *)data;\n\n\tcache = container_of(A, struct zchunk_cache, node);\n\n\tif (CHUNK_CHECK_OFFSET(cache->chunk, offset))\n\t\treturn 0;\n\n\tif (cache->chunk->offset < offset)\n\t\treturn -1;\n\n\treturn 1;\n}\n\nstatic int init_cpu(struct tracecmd_input *handle, int cpu)\n{\n\tstruct cpu_data *cpu_data = &handle->cpu_data[cpu];\n\tint ret;\n\tint i;\n\n\tif (handle->cpu_compressed && cpu_data->file_size > 0) {\n\t\tif (handle->read_zpage)\n\t\t\tret = init_cpu_zpage(handle, cpu);\n\t\telse\n\t\t\tret = init_cpu_zfile(handle, cpu);\n\t\tif (ret)\n\t\t\treturn ret;\n\t} else {\n\t\tcpu_data->offset = cpu_data->file_offset;\n\t\tcpu_data->size = cpu_data->file_size;\n\t}\n\tcpu_data->timestamp = 0;\n\n\tlist_head_init(&cpu_data->page_maps);\n\n\ttcmd_rbtree_init(&cpu_data->compress.cache, compress_cmp, compress_search);\n\n\tif (!cpu_data->size) {\n\t\ttracecmd_info(\"CPU %d is empty\", cpu);\n\t\treturn 0;\n\t}\n\n\tcpu_data->nr_pages = (cpu_data->size + handle->page_size - 1) / handle->page_size;\n\tif (!cpu_data->nr_pages)\n\t\tcpu_data->nr_pages = 1;\n\tcpu_data->pages = calloc(cpu_data->nr_pages, sizeof(*cpu_data->pages));\n\tif (!cpu_data->pages)\n\t\treturn -1;\n\n\tif (handle->use_pipe) {\n\t\t/* Just make a page, it will be nuked later */\n\t\tcpu_data->page = malloc(sizeof(*cpu_data->page));\n\t\tif (!cpu_data->page)\n\t\t\tgoto fail;\n\n\t\tmemset(cpu_data->page, 0, sizeof(*cpu_data->page));\n\t\tcpu_data->pages[0] = cpu_data->page;\n\t\tcpu_data->page_cnt = 1;\n\t\tcpu_data->page->ref_count = 1;\n\t\treturn 0;\n\t}\n\n\tcpu_data->page = allocate_page(handle, cpu, cpu_data->offset);\n\tif (!cpu_data->page && !handle->read_page) {\n\t\tperror(\"mmap\");\n\t\tfprintf(stderr, \"Can not mmap file, will read instead\\n\");\n\n\t\tif (cpu) {\n\t\t\t/*\n\t\t\t * If the other CPUs had size and was able to mmap\n\t\t\t * then bail.\n\t\t\t */\n\t\t\tfor (i = 0; i < cpu; i++) {\n\t\t\t\tif (handle->cpu_data[i].size)\n\t\t\t\t\tgoto fail;\n\t\t\t}\n\t\t}\n\n\t\t/* try again without mmapping, just read it directly */\n\t\thandle->read_page = true;\n\t\tcpu_data->page = allocate_page(handle, cpu, cpu_data->offset);\n\t\tif (!cpu_data->page)\n\t\t\t/* Still no luck, bail! */\n\t\t\tgoto fail;\n\t}\n\n\tif (update_page_info(handle, cpu))\n\t\tgoto fail;\n\tcpu_data->first_ts = cpu_data->timestamp;\n\n\treturn 0;\n fail:\n\tfree(cpu_data->pages);\n\tcpu_data->pages = NULL;\n\tfree(cpu_data->page);\n\tcpu_data->page = NULL;\n\treturn -1;\n}\n\nvoid tracecmd_set_ts_offset(struct tracecmd_input *handle,\n\t\t\t    long long offset)\n{\n\thandle->ts_offset = offset;\n}\n\n/**\n * tracecmd_add_ts_offset - Add value to the offset which will be applied to the timestamps of all\n *\t\t\t    events from given trace file\n * @handle: input handle to the trace.dat file\n * @offset: value, that will be added to the offset\n */\nvoid tracecmd_add_ts_offset(struct tracecmd_input *handle,\n\t\t\t    long long offset)\n{\n\thandle->ts_offset += offset;\n}\n\nvoid tracecmd_set_ts2secs(struct tracecmd_input *handle,\n\t\t\t unsigned long long hz)\n{\n\tdouble ts2secs;\n\n\tts2secs = (double)NSEC_PER_SEC / (double)hz;\n\thandle->ts2secs = ts2secs;\n\thandle->use_trace_clock = false;\n}\n\nstatic int tsync_offset_cmp(const void *a, const void *b)\n{\n\tstruct ts_offset_sample *ts_a = (struct ts_offset_sample *)a;\n\tstruct ts_offset_sample *ts_b = (struct ts_offset_sample *)b;\n\n\tif (ts_a->time > ts_b->time)\n\t\treturn 1;\n\tif (ts_a->time < ts_b->time)\n\t\treturn -1;\n\treturn 0;\n}\n\n#define safe_read(R, C)\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\\\n\t\tif ((C) > size)\t\t\t\t\\\n\t\t\treturn -EFAULT;\t\t\t\\\n\t\t(R) = tep_read_number(tep, buf, (C));\t\\\n\t\tbuf += (C);\t\t\t\t\\\n\t\tsize -= (C);\t\t\t\t\\\n\t} while (0)\n\n#define safe_read_loop(type)\t\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tint ii;\t\t\t\t\t\t\t\\\n\t\tfor (ii = 0; ii < ts_offsets->ts_samples_count; ii++)\t\\\n\t\t\tsafe_read(ts_offsets->ts_samples[ii].type, 8);\t\\\n\t} while (0)\n\nstatic int tsync_cpu_offsets_load(struct tracecmd_input *handle, char *buf, int size)\n{\n\tstruct tep_handle *tep = handle->pevent;\n\tstruct timesync_offsets *ts_offsets;\n\tint i, j, k;\n\n\tsafe_read(handle->host.cpu_count, 4);\n\thandle->host.ts_offsets = calloc(handle->host.cpu_count,\n\t\t\t\t\t sizeof(struct timesync_offsets));\n\tif (!handle->host.ts_offsets)\n\t\treturn -ENOMEM;\n\tfor (i = 0; i < handle->host.cpu_count; i++) {\n\t\tts_offsets = &handle->host.ts_offsets[i];\n\t\tsafe_read(ts_offsets->ts_samples_count, 4);\n\t\tts_offsets->ts_samples = calloc(ts_offsets->ts_samples_count,\n\t\t\t\t\t\tsizeof(struct ts_offset_sample));\n\t\tif (!ts_offsets->ts_samples)\n\t\t\treturn -ENOMEM;\n\t\tsafe_read_loop(time);\n\t\tsafe_read_loop(offset);\n\t\tsafe_read_loop(scaling);\n\t}\n\n\tif (size > 0) {\n\t\tfor (i = 0; i < handle->host.cpu_count; i++) {\n\t\t\tts_offsets = &handle->host.ts_offsets[i];\n\t\t\tsafe_read_loop(fraction);\n\t\t}\n\t}\n\n\tfor (i = 0; i < handle->host.cpu_count; i++) {\n\t\tts_offsets = &handle->host.ts_offsets[i];\n\t\tqsort(ts_offsets->ts_samples, ts_offsets->ts_samples_count,\n\t\t      sizeof(struct ts_offset_sample), tsync_offset_cmp);\n\t\t/* Filter possible samples with equal time */\n\t\tfor (k = 0, j = 0; k < ts_offsets->ts_samples_count; k++) {\n\t\t\tif (k == 0 || ts_offsets->ts_samples[k].time != ts_offsets->ts_samples[k-1].time)\n\t\t\t\tts_offsets->ts_samples[j++] = ts_offsets->ts_samples[k];\n\t\t}\n\t\tts_offsets->ts_samples_count = j;\n\t}\n\n\treturn 0;\n}\n\nstatic void trace_tsync_offset_free(struct host_trace_info *host)\n{\n\tint i;\n\n\tif (host->ts_offsets) {\n\t\tfor (i = 0; i < host->cpu_count; i++)\n\t\t\tfree(host->ts_offsets[i].ts_samples);\n\t\tfree(host->ts_offsets);\n\t\thost->ts_offsets = NULL;\n\t}\n}\n\nstatic int trace_pid_map_cmp(const void *a, const void *b)\n{\n\tstruct tracecmd_proc_addr_map *m_a = (struct tracecmd_proc_addr_map *)a;\n\tstruct tracecmd_proc_addr_map *m_b = (struct tracecmd_proc_addr_map *)b;\n\n\tif (m_a->start > m_b->start)\n\tif (m_a->start < m_b->start)\n\t\treturn -1;\n\treturn 0;\n}\n\nstatic void procmap_free(struct pid_addr_maps *maps)\n{\n\tint i;\n\n\tif (!maps)\n\t\treturn;\n\tif (maps->lib_maps) {\n\t\tfor (i = 0; i < maps->nr_lib_maps; i++)\n\t\t\tfree(maps->lib_maps[i].lib_name);\n\t\tfree(maps->lib_maps);\n\t}\n\tfree(maps->proc_name);\n\tfree(maps);\n}\n\nstatic void trace_guests_free(struct tracecmd_input *handle)\n{\n\tstruct guest_trace_info *guest;\n\n\twhile (handle->guest) {\n\t\tguest = handle->guest;\n\t\thandle->guest = handle->guest->next;\n\t\tfree(guest->name);\n\t\tfree(guest->cpu_pid);\n\t\tfree(guest);\n\t}\n}\n\nstatic int trace_guest_load(struct tracecmd_input *handle, char *buf, int size)\n{\n\tstruct guest_trace_info *guest = NULL;\n\tint cpu;\n\tint i;\n\n\tguest = calloc(1, sizeof(struct guest_trace_info));\n\tif (!guest)\n\t\tgoto error;\n\n\t/*\n\t * Guest name, null terminated string\n\t * long long (8 bytes) trace-id\n\t * int (4 bytes) number of guest CPUs\n\t * array of size number of guest CPUs:\n\t *\tint (4 bytes) Guest CPU id\n\t *\tint (4 bytes) Host PID, running the guest CPU\n\t */\n\n\tguest->name = strndup(buf, size);\n\tif (!guest->name)\n\t\tgoto error;\n\tbuf += strlen(guest->name) + 1;\n\tsize -= strlen(guest->name) + 1;\n\n\tif (size < sizeof(long long))\n\t\tgoto error;\n\tguest->trace_id = tep_read_number(handle->pevent, buf, sizeof(long long));\n\tbuf += sizeof(long long);\n\tsize -= sizeof(long long);\n\n\tif (size < sizeof(int))\n\t\tgoto error;\n\tguest->vcpu_count = tep_read_number(handle->pevent, buf, sizeof(int));\n\tbuf += sizeof(int);\n\tsize -= sizeof(int);\n\n\tguest->cpu_pid = calloc(guest->vcpu_count, sizeof(int));\n\tif (!guest->cpu_pid)\n\t\tgoto error;\n\n\tfor (i = 0; i < guest->vcpu_count; i++) {\n\t\tif (size < 2 * sizeof(int))\n\t\t\tgoto error;\n\t\tcpu = tep_read_number(handle->pevent, buf, sizeof(int));\n\t\tbuf += sizeof(int);\n\t\tif (cpu >= guest->vcpu_count)\n\t\t\tgoto error;\n\t\tguest->cpu_pid[cpu] = tep_read_number(handle->pevent,\n\t\t\t\t\t\t      buf, sizeof(int));\n\t\tbuf += sizeof(int);\n\t\tsize -= 2 * sizeof(int);\n\t}\n\n\tguest->next = handle->guest;\n\thandle->guest = guest;\n\treturn 0;\n\nerror:\n\tif (guest) {\n\t\tfree(guest->cpu_pid);\n\t\tfree(guest->name);\n\t\tfree(guest);\n\t}\n\treturn -1;\n}\n\n/* Needs to be a constant, and 4K should be good enough */\n#define STR_PROCMAP_LINE_MAX\t4096\nstatic int trace_pid_map_load(struct tracecmd_input *handle, char *buf)\n{\n\tstruct pid_addr_maps *maps = NULL;\n\tchar mapname[STR_PROCMAP_LINE_MAX+1];\n\tchar *line;\n\tint res;\n\tint ret;\n\tint i;\n\n\tmaps = calloc(1, sizeof(*maps));\n\tif (!maps)\n\t\treturn -ENOMEM;\n\n\tret  = -EINVAL;\n\tline = strchr(buf, '\\n');\n\tif (!line)\n\t\tgoto out_fail;\n\n\t*line = '\\0';\n\tif (strlen(buf) > STR_PROCMAP_LINE_MAX)\n\t\tgoto out_fail;\n\n\tres = sscanf(buf, \"%x %x %\"STRINGIFY(STR_PROCMAP_LINE_MAX)\"s\", &maps->pid, &maps->nr_lib_maps, mapname);\n\tif (res != 3)\n\t\tgoto out_fail;\n\n\tret  = -ENOMEM;\n\tmaps->proc_name = strdup(mapname);\n\tif (!maps->proc_name)\n\t\tgoto out_fail;\n\n\tmaps->lib_maps = calloc(maps->nr_lib_maps, sizeof(struct tracecmd_proc_addr_map));\n\tif (!maps->lib_maps)\n\t\tgoto out_fail;\n\n\tbuf = line + 1;\n\tline = strchr(buf, '\\n');\n\tfor (i = 0; i < maps->nr_lib_maps; i++) {\n\t\tif (!line)\n\t\t\tbreak;\n\t\t*line = '\\0';\n\t\tif (strlen(buf) > STR_PROCMAP_LINE_MAX)\n\t\t\tbreak;\n\t\tres = sscanf(buf, \"%zx %zx %s\", &maps->lib_maps[i].start,\n\t\t\t     &maps->lib_maps[i].end, mapname);\n\t\tif (res != 3)\n\t\t\tbreak;\n\t\tmaps->lib_maps[i].lib_name = strdup(mapname);\n\t\tif (!maps->lib_maps[i].lib_name)\n\t\t\tgoto out_fail;\n\t\tbuf = line + 1;\n\t\tline = strchr(buf, '\\n');\n\t}\n\n\tret  = -EINVAL;\n\tif (i != maps->nr_lib_maps)\n\t\tgoto out_fail;\n\n\tqsort(maps->lib_maps, maps->nr_lib_maps,\n\t      sizeof(*maps->lib_maps), trace_pid_map_cmp);\n\n\tmaps->next = handle->pid_maps;\n\thandle->pid_maps = maps;\n\n\treturn 0;\n\nout_fail:\n\tprocmap_free(maps);\n\treturn ret;\n}\n\nstatic void trace_pid_map_free(struct pid_addr_maps *maps)\n{\n\tstruct pid_addr_maps *del;\n\n\twhile (maps) {\n\t\tdel = maps;\n\t\tmaps = maps->next;\n\t\tprocmap_free(del);\n\t}\n}\n\nstatic int trace_pid_map_search(const void *a, const void *b)\n{\n\tstruct tracecmd_proc_addr_map *key = (struct tracecmd_proc_addr_map *)a;\n\tstruct tracecmd_proc_addr_map *map = (struct tracecmd_proc_addr_map *)b;\n\n\tif (key->start >= map->end)\n\t\treturn 1;\n\tif (key->start < map->start)\n\t\treturn -1;\n\treturn 0;\n}\n\n/**\n * tracecmd_search_task_map - Search task memory address map\n * @handle: input handle to the trace.dat file\n * @pid: pid of the task\n * @addr: address from the task memory space.\n *\n * Map of the task memory can be saved in the trace.dat file, using the option\n * \"--proc-map\". If there is such information, this API can be used to look up\n * into this memory map to find what library is loaded at the given @addr.\n *\n * A pointer to struct tracecmd_proc_addr_map is returned, containing the name\n * of the library at given task @addr and the library start and end addresses.\n */\nstruct tracecmd_proc_addr_map *\ntracecmd_search_task_map(struct tracecmd_input *handle,\n\t\t\t int pid, unsigned long long addr)\n{\n\tstruct tracecmd_proc_addr_map *lib;\n\tstruct tracecmd_proc_addr_map key;\n\tstruct pid_addr_maps *maps;\n\n\tif (!handle || !handle->pid_maps)\n\t\treturn NULL;\n\n\tmaps = handle->pid_maps;\n\twhile (maps) {\n\t\tif (maps->pid == pid)\n\t\t\tbreak;\n\t\tmaps = maps->next;\n\t}\n\tif (!maps || !maps->nr_lib_maps || !maps->lib_maps)\n\t\treturn NULL;\n\tkey.start = addr;\n\tlib = bsearch(&key, maps->lib_maps, maps->nr_lib_maps,\n\t\t      sizeof(*maps->lib_maps), trace_pid_map_search);\n\n\treturn lib;\n}\n\n__hidden unsigned int tcmd_get_meta_strings_size(struct tracecmd_input *handle)\n{\n\treturn handle->strings_size;\n}\n\n__hidden unsigned long long tcmd_get_last_option_offset(struct tracecmd_input *handle)\n{\n\treturn handle->options_last_offset;\n}\n\nstatic int handle_option_done(struct tracecmd_input *handle, char *buf, int size)\n{\n\tunsigned long long offset;\n\n\tif (size < 8)\n\t\treturn -1;\n\n\toffset = lseek(handle->fd, 0, SEEK_CUR);\n\tif (offset >= size)\n\t\thandle->options_last_offset = offset - size;\n\n\toffset = tep_read_number(handle->pevent, buf, 8);\n\tif (!offset)\n\t\treturn 0;\n\n\tif (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\treturn handle_options(handle);\n}\n\nstatic inline int save_read_number(struct tep_handle *tep, char *data, int *data_size,\n\t\t\t\t   int *read_pos, int bytes, unsigned long long *num)\n{\n\tif (bytes > *data_size)\n\t\treturn -1;\n\n\t*num = tep_read_number(tep, (data + *read_pos), bytes);\n\t*read_pos += bytes;\n\t*data_size -= bytes;\n\treturn 0;\n}\n\nstatic inline char *save_read_string(char *data, int *data_size, int *read_pos)\n{\n\tchar *str;\n\n\tif (*data_size < 1)\n\t\treturn NULL;\n\n\tstr = strdup(data + *read_pos);\n\tif (!str)\n\t\treturn NULL;\n\t*data_size -= (strlen(str) + 1);\n\tif (*data_size < 0) {\n\t\tfree(str);\n\t\treturn NULL;\n\t}\n\t*read_pos += (strlen(str) + 1);\n\n\treturn str;\n}\n\nstatic int handle_buffer_option(struct tracecmd_input *handle,\n\t\t\t\tunsigned short id, char *data, int size)\n{\n\tstruct input_buffer_instance *buff;\n\tstruct cpu_file_data *cpu_data;\n\tunsigned long long tmp;\n\tlong long max_cpu = -1;\n\tint rsize = 0;\n\tchar *name;\n\tint i;\n\n\tif (save_read_number(handle->pevent, data, &size, &rsize, 8, &tmp))\n\t\treturn -1;\n\n\tname = save_read_string(data, &size, &rsize);\n\tif (!name)\n\t\treturn -1;\n\n\tif (*name == '\\0') {\n\t\t/* top buffer */\n\t\tbuff = &handle->top_buffer;\n\t} else {\n\t\tbuff = realloc(handle->buffers, sizeof(*handle->buffers) * (handle->nr_buffers + 1));\n\t\tif (!buff) {\n\t\t\tfree(name);\n\t\t\treturn -1;\n\t\t}\n\t\thandle->buffers = buff;\n\t\thandle->nr_buffers++;\n\n\t\tbuff = &handle->buffers[handle->nr_buffers - 1];\n\t}\n\tmemset(buff, 0, sizeof(struct input_buffer_instance));\n\tbuff->name = name;\n\tbuff->offset = tmp;\n\n\tif (!HAS_SECTIONS(handle))\n\t\treturn 0;\n\n\t/* file sections specific data */\n\tbuff->clock = save_read_string(data, &size, &rsize);\n\tif (!buff->clock)\n\t\treturn -1;\n\n\tif (*name == '\\0' && !handle->trace_clock)\n\t\thandle->trace_clock = strdup(buff->clock);\n\n\tif (id == TRACECMD_OPTION_BUFFER) {\n\t\tif (save_read_number(handle->pevent, data, &size, &rsize, 4, &tmp))\n\t\t\treturn -1;\n\t\tbuff->page_size = tmp;\n\n\t\tif (save_read_number(handle->pevent, data, &size, &rsize, 4, &tmp))\n\t\t\treturn -1;\n\t\tbuff->cpus = tmp;\n\t\tif (!buff->cpus)\n\t\t\treturn 0;\n\t\tcpu_data = calloc(buff->cpus, sizeof(*cpu_data));\n\t\tif (!cpu_data)\n\t\t\treturn -1;\n\t\tfor (i = 0; i < buff->cpus; i++) {\n\t\t\tif (save_read_number(handle->pevent, data, &size, &rsize, 4, &tmp))\n\t\t\t\tgoto fail;\n\t\t\tif ((long long)tmp > max_cpu)\n\t\t\t\tmax_cpu = tmp;\n\t\t\tcpu_data[i].cpu = tmp;\n\t\t\tif (save_read_number(handle->pevent, data,\n\t\t\t\t\t     &size, &rsize, 8, &cpu_data[i].offset))\n\t\t\t\tgoto fail;\n\t\t\tif (save_read_number(handle->pevent, data,\n\t\t\t\t\t     &size, &rsize, 8, &cpu_data[i].size))\n\t\t\t\tgoto fail;\n\t\t}\n\t\tif (buff->cpus == max_cpu + 1) {\n\t\t\t/* Check to make sure cpus match the index */\n\t\t\tfor (i = 0; i < buff->cpus; i++) {\n\t\t\t\tif (cpu_data[i].cpu != i)\n\t\t\t\t\tgoto copy_buffer;\n\t\t\t}\n\t\t\tbuff->cpu_data = cpu_data;\n\t\t} else {\n copy_buffer:\n\t\t\tbuff->cpu_data = calloc(max_cpu + 1, sizeof(*cpu_data));\n\t\t\tif (!buff->cpu_data)\n\t\t\t\tgoto fail;\n\t\t\tfor (i = 0; i < buff->cpus; i++) {\n\t\t\t\tif (buff->cpu_data[cpu_data[i].cpu].size) {\n\t\t\t\t\ttracecmd_warning(\"More than one buffer defined for CPU %d (buffer %d)\\n\",\n\t\t\t\t\t\t\t cpu_data[i].cpu, i);\n\t\t\t\t\tgoto fail;\n\t\t\t\t}\n\t\t\t\tbuff->cpu_data[cpu_data[i].cpu] = cpu_data[i];\n\t\t\t}\n\t\t\tbuff->cpus = max_cpu + 1;\n\t\t\tfree(cpu_data);\n\t\t}\n\t} else {\n\t\tbuff->latency = true;\n\t}\n\treturn 0;\nfail:\n\tfree(cpu_data);\n\treturn -1;\n}\n\nstatic int handle_options(struct tracecmd_input *handle)\n{\n\tlong long offset;\n\tunsigned short option;\n\tunsigned int size;\n\tunsigned short id, flags;\n\tchar *cpustats = NULL;\n\tstruct hook_list *hook;\n\tbool compress = false;\n\tchar *buf = NULL;\n\tint cpus;\n\tint ret;\n\n\tif (!HAS_SECTIONS(handle)) {\n\t\thandle->options_start = lseek(handle->fd, 0, SEEK_CUR);\n\t} else {\n\t\tif (read_section_header(handle, &id, &flags, NULL, NULL))\n\t\t\treturn -1;\n\t\tif (id != TRACECMD_OPTION_DONE)\n\t\t\treturn -1;\n\t\tif (flags & TRACECMD_SEC_FL_COMPRESS)\n\t\t\tcompress = true;\n\t}\n\n\tif (compress && tcmd_in_uncompress_block(handle))\n\t\treturn -1;\n\n\tfor (;;) {\n\t\tret = read2(handle, &option);\n\t\tif (ret)\n\t\t\tgoto out;\n\n\t\tif (!HAS_SECTIONS(handle) && option == TRACECMD_OPTION_DONE)\n\t\t\tbreak;\n\n\t\t/* next 4 bytes is the size of the option */\n\t\tret = read4(handle, &size);\n\t\tif (ret)\n\t\t\tgoto out;\n\t\tfree(buf);\n\t\tbuf = malloc(size);\n\t\tif (!buf) {\n\t\t\tret = -ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\t\tret = do_read_check(handle, buf, size);\n\t\tif (ret)\n\t\t\tgoto out;\n\n\t\tswitch (option) {\n\t\tcase TRACECMD_OPTION_DATE:\n\t\t\t/*\n\t\t\t * A time has been mapped that is the\n\t\t\t * difference between the timestamps and\n\t\t\t * gtod. It is stored as ASCII with '0x'\n\t\t\t * appended.\n\t\t\t */\n\t\t\tif (handle->flags &\n\t\t\t    (TRACECMD_FL_IGNORE_DATE | TRACECMD_FL_RAW_TS))\n\t\t\t\tbreak;\n\t\t\toffset = strtoll(buf, NULL, 0);\n\t\t\t/* Convert from micro to nano */\n\t\t\toffset *= 1000;\n\t\t\thandle->ts_offset += offset;\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_OFFSET:\n\t\t\t/*\n\t\t\t * Similar to date option, but just adds an\n\t\t\t * offset to the timestamp.\n\t\t\t */\n\t\t\tif (handle->flags & TRACECMD_FL_RAW_TS)\n\t\t\t\tbreak;\n\t\t\toffset = strtoll(buf, NULL, 0);\n\t\t\thandle->ts_offset += offset;\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_TIME_SHIFT:\n\t\t\t/*\n\t\t\t * long long int (8 bytes) trace session ID\n\t\t\t * int (4 bytes) protocol flags.\n\t\t\t * int (4 bytes) CPU count.\n\t\t\t * array of size [CPU count]:\n\t\t\t * [\n\t\t\t *  int (4 bytes) count of timestamp offsets.\n\t\t\t *  long long array of size [count] of times,\n\t\t\t *      when the offsets were calculated.\n\t\t\t *  long long array of size [count] of timestamp offsets.\n\t\t\t *  long long array of size [count] of timestamp scaling ratios.*\n\t\t\t * ]\n\t\t\t * array of size [CPU count]:\n\t\t\t * [\n\t\t\t *  long long array of size [count] of timestamp scaling fraction bits.*\n\t\t\t * ]*\n\t\t\t */\n\t\t\tif (size < 16 || (handle->flags & TRACECMD_FL_RAW_TS))\n\t\t\t\tbreak;\n\t\t\thandle->host.peer_trace_id = tep_read_number(handle->pevent,\n\t\t\t\t\t\t\t\t     buf, 8);\n\t\t\thandle->host.flags = tep_read_number(handle->pevent,\n\t\t\t\t\t\t\t     buf + 8, 4);\n\t\t\tret = tsync_cpu_offsets_load(handle, buf + 12, size - 12);\n\t\t\tif (ret < 0)\n\t\t\t\tgoto out;\n\t\t\ttracecmd_enable_tsync(handle, true);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_CPUSTAT:\n\t\t\tbuf[size-1] = '\\n';\n\t\t\tcpustats = realloc(handle->cpustats,\n\t\t\t\t\t   handle->cpustats_size + size + 1);\n\t\t\tif (!cpustats) {\n\t\t\t\tret = -ENOMEM;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tmemcpy(cpustats + handle->cpustats_size, buf, size);\n\t\t\thandle->cpustats_size += size;\n\t\t\tcpustats[handle->cpustats_size] = 0;\n\t\t\thandle->cpustats = cpustats;\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_BUFFER:\n\t\tcase TRACECMD_OPTION_BUFFER_TEXT:\n\t\t\tret = handle_buffer_option(handle, option, buf, size);\n\t\t\tif (ret < 0)\n\t\t\t\tgoto out;\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_TRACECLOCK:\n\t\t\ttracecmd_parse_trace_clock(handle, buf, size);\n\t\t\tif (!handle->ts2secs)\n\t\t\t\thandle->use_trace_clock = true;\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_UNAME:\n\t\t\thandle->uname = strdup(buf);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_VERSION:\n\t\t\thandle->version = strdup(buf);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_HOOK:\n\t\t\thook = tracecmd_create_event_hook(buf);\n\t\t\thook->next = handle->hooks;\n\t\t\thandle->hooks = hook;\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_CPUCOUNT:\n\t\t\tcpus = *(int *)buf;\n\t\t\thandle->cpus = tep_read_number(handle->pevent, &cpus, 4);\n\t\t\tif (handle->cpus > handle->max_cpu)\n\t\t\t\thandle->max_cpu = handle->cpus;\n\t\t\ttep_set_cpus(handle->pevent, handle->cpus);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_PROCMAPS:\n\t\t\tif (buf[size-1] == '\\0')\n\t\t\t\ttrace_pid_map_load(handle, buf);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_TRACEID:\n\t\t\tif (size < 8)\n\t\t\t\tbreak;\n\t\t\thandle->trace_id = tep_read_number(handle->pevent,\n\t\t\t\t\t\t\t   buf, 8);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_GUEST:\n\t\t\ttrace_guest_load(handle, buf, size);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_TSC2NSEC:\n\t\t\tif (size < 16 || (handle->flags & TRACECMD_FL_RAW_TS))\n\t\t\t\tbreak;\n\t\t\thandle->tsc_calc.mult = tep_read_number(handle->pevent,\n\t\t\t\t\t\t\t\tbuf, 4);\n\t\t\thandle->tsc_calc.shift = tep_read_number(handle->pevent,\n\t\t\t\t\t\t\t\t buf + 4, 4);\n\t\t\thandle->tsc_calc.offset = tep_read_number(handle->pevent,\n\t\t\t\t\t\t\t\t  buf + 8, 8);\n\t\t\tif (!(handle->flags & TRACECMD_FL_RAW_TS))\n\t\t\t\thandle->flags |= TRACECMD_FL_IN_USECS;\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_LAST_BOOT_INFO:\n\t\t\t{\n\t\t\t\tchar *file;\n\n\t\t\t\t/* Skip the name of the instance (for now) */\n\t\t\t\tfile = strchr(buf, ':');\n\t\t\t\tif (!file)\n\t\t\t\t\tbreak;\n\t\t\t\tfile++;\n\t\t\t\ttep_parse_last_boot_info(handle->pevent, file);\n\t\t\t\tbreak;\n\t\t\t}\n\t\tcase TRACECMD_OPTION_HEADER_INFO:\n\t\tcase TRACECMD_OPTION_FTRACE_EVENTS:\n\t\tcase TRACECMD_OPTION_EVENT_FORMATS:\n\t\tcase TRACECMD_OPTION_KALLSYMS:\n\t\tcase TRACECMD_OPTION_PRINTK:\n\t\tcase TRACECMD_OPTION_CMDLINES:\n\t\tcase TRACECMD_OPTION_BTF_FILE:\n\t\tcase TRACECMD_OPTION_MODULES_FILE:\n\t\t\tif (size < 8)\n\t\t\t\tbreak;\n\t\t\tsection_add_or_update(handle, option, -1,\n\t\t\t\t\t      tep_read_number(handle->pevent, buf, 8), 0);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_DONE:\n\t\t\tif (compress)\n\t\t\t\ttcmd_in_uncompress_reset(handle);\n\t\t\tret = handle_option_done(handle, buf, size);\n\t\t\tfree(buf);\n\t\t\treturn ret;\n\n\t\tdefault:\n\t\t\ttracecmd_warning(\"unknown option %d\", option);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tret = 0;\n\nout:\n\tfree(buf);\n\tif (compress)\n\t\ttcmd_in_uncompress_reset(handle);\n\treturn ret;\n}\n\nstatic int read_options_type(struct tracecmd_input *handle)\n{\n\tchar buf[10];\n\n\tif (CHECK_READ_STATE(handle, TRACECMD_FILE_CPU_LATENCY))\n\t\treturn 0;\n\n\tif (do_read_check(handle, buf, 10))\n\t\treturn -1;\n\n\t/* check if this handles options */\n\tif (strncmp(buf, \"options\", 7) == 0) {\n\t\tif (handle_options(handle) < 0)\n\t\t\treturn -1;\n\t\thandle->file_state = TRACECMD_FILE_OPTIONS;\n\t\tif (do_read_check(handle, buf, 10))\n\t\t\treturn -1;\n\t}\n\n\t/*\n\t * Check if this is a latency report or flyrecord.\n\t */\n\tif (strncmp(buf, \"latency\", 7) == 0)\n\t\thandle->file_state = TRACECMD_FILE_CPU_LATENCY;\n\telse if (strncmp(buf, \"flyrecord\", 9) == 0)\n\t\thandle->file_state = TRACECMD_FILE_CPU_FLYRECORD;\n\telse\n\t\treturn -1;\n\n\treturn 0;\n}\n\nint tracecmd_latency_data_read(struct tracecmd_input *handle, char **buf, size_t *size)\n{\n\tstruct cpu_zdata *zdata = &handle->latz;\n\tvoid *data;\n\tint rsize;\n\tint fd = -1;\n\tint id;\n\n\tif (!handle || !buf || !size)\n\t\treturn -1;\n\tif (handle->file_state != TRACECMD_FILE_CPU_LATENCY)\n\t\treturn -1;\n\n\tif (!handle->cpu_compressed) {\n\t\tfd = handle->fd;\n\t} else if (!handle->read_zpage) {\n\t\tif (zdata->fd < 0)\n\t\t\treturn -1;\n\t\tfd = zdata->fd;\n\t}\n\n\t/* Read data from a file */\n\tif (fd >= 0) {\n\t\tif (!(*buf)) {\n\t\t\t*size = BUFSIZ;\n\t\t\t*buf = malloc(*size);\n\t\t\tif (!(*buf))\n\t\t\t\treturn -1;\n\t\t}\n\t\treturn do_read_fd(fd, *buf, *size);\n\t}\n\n\t/* Uncompress data in memory */\n\tif (zdata->last_chunk >= zdata->count)\n\t\treturn 0;\n\n\tid = zdata->last_chunk;\n\tif (!*buf || *size < zdata->chunks[id].size) {\n\t\tdata = realloc(*buf, zdata->chunks[id].size);\n\t\tif (!data)\n\t\t\treturn -1;\n\t\t*buf = data;\n\t\t*size = zdata->chunks[id].size;\n\t}\n\n\tif (tracecmd_uncompress_chunk(handle->compress, &zdata->chunks[id], *buf))\n\t\treturn -1;\n\n\trsize = zdata->chunks[id].size;\n\tzdata->last_chunk++;\n\treturn rsize;\n}\n\nstatic int init_cpu_data(struct tracecmd_input *handle)\n{\n\tenum kbuffer_long_size long_size;\n\tenum kbuffer_endian endian;\n\tunsigned long long max_size = 0;\n\tunsigned long long pages;\n\tint cpu;\n\n\t/* We expect this to be flyrecord */\n\tif (handle->file_state != TRACECMD_FILE_CPU_FLYRECORD)\n\t\treturn -1;\n\n\tif (force_read)\n\t\thandle->read_page = true;\n\n\tif (handle->long_size == 8)\n\t\tlong_size = KBUFFER_LSIZE_8;\n\telse\n\t\tlong_size = KBUFFER_LSIZE_4;\n\n\tif (tep_is_file_bigendian(handle->pevent))\n\t\tendian = KBUFFER_ENDIAN_BIG;\n\telse\n\t\tendian = KBUFFER_ENDIAN_LITTLE;\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\thandle->cpu_data[cpu].compress.fd = -1;\n\t\thandle->cpu_data[cpu].kbuf = kbuffer_alloc(long_size, endian);\n\t\tif (!handle->cpu_data[cpu].kbuf)\n\t\t\tgoto out_free;\n\t\tif (tep_is_old_format(handle->pevent))\n\t\t\tkbuffer_set_old_format(handle->cpu_data[cpu].kbuf);\n\n\t\tif (handle->cpu_data[cpu].file_size > max_size)\n\t\t\tmax_size = handle->cpu_data[cpu].file_size;\n\t}\n\n\t/* Calculate about a meg of pages for buffering */\n\tpages = handle->page_size ? max_size / handle->page_size : 0;\n\tif (!pages)\n\t\tpages = 1;\n\tpages = normalize_size(pages);\n\thandle->page_map_size = handle->page_size * pages;\n\tif (handle->page_map_size < handle->page_size)\n\t\thandle->page_map_size = handle->page_size;\n\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\tif (init_cpu(handle, cpu))\n\t\t\tgoto out_free;\n\t}\n\n\treturn 0;\n\n out_free:\n\tfor ( ; cpu >= 0; cpu--) {\n\t\tfree_page(handle, cpu);\n\t\tkbuffer_free(handle->cpu_data[cpu].kbuf);\n\t\thandle->cpu_data[cpu].kbuf = NULL;\n\t}\n\treturn -1;\n}\n\nint init_latency_data(struct tracecmd_input *handle)\n{\n\tsize_t wsize;\n\tint ret;\n\n\tif (!handle->cpu_compressed)\n\t\treturn 0;\n\n\tif (handle->read_zpage) {\n\t\thandle->latz.count = tracecmd_load_chunks_info(handle->compress, &handle->latz.chunks);\n\t\tif (handle->latz.count < 0)\n\t\t\treturn -1;\n\t} else {\n\t\tstrcpy(handle->latz.file, COMPR_TEMP_FILE);\n\t\thandle->latz.fd = mkstemp(handle->latz.file);\n\t\tif (handle->latz.fd < 0)\n\t\t\treturn -1;\n\n\t\tret = tracecmd_uncompress_copy_to(handle->compress, handle->latz.fd, NULL, &wsize);\n\t\tif (ret)\n\t\t\treturn -1;\n\n\t\tlseek(handle->latz.fd, 0, SEEK_SET);\n\t}\n\n\treturn 0;\n}\n\nstatic int init_buffer_cpu_data(struct tracecmd_input *handle, struct input_buffer_instance *buffer)\n{\n\tunsigned long long offset;\n\tunsigned long long size;\n\tunsigned short id, flags;\n\tint cpu;\n\n\tif (handle->cpu_data)\n\t\treturn -1;\n\n\tif (lseek(handle->fd, buffer->offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\tif (read_section_header(handle, &id, &flags, NULL, NULL))\n\t\treturn -1;\n\tif (flags & TRACECMD_SEC_FL_COMPRESS)\n\t\thandle->cpu_compressed = true;\n\tif (buffer->latency) {\n\t\thandle->file_state = TRACECMD_FILE_CPU_LATENCY;\n\t\treturn init_latency_data(handle) == 0 ? 1 : -1;\n\t}\n\thandle->file_state = TRACECMD_FILE_CPU_FLYRECORD;\n\thandle->cpus = buffer->cpus;\n\tif (handle->max_cpu < handle->cpus)\n\t\thandle->max_cpu = handle->cpus;\n\n\thandle->cpu_data = calloc(handle->cpus, sizeof(*handle->cpu_data));\n\tif (!handle->cpu_data)\n\t\treturn -1;\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\thandle->cpu_data[cpu].cpu = buffer->cpu_data[cpu].cpu;\n\t\toffset = buffer->cpu_data[cpu].offset;\n\t\tsize = buffer->cpu_data[cpu].size;\n\t\thandle->cpu_data[cpu].file_offset = offset;\n\t\thandle->cpu_data[cpu].file_size = size;\n\t\tif (size && (offset + size > handle->total_file_size)) {\n\t\t\t/* this happens if the file got truncated */\n\t\t\tprintf(\"File possibly truncated. \"\n\t\t\t\t\"Need at least %llu, but file size is %zu.\\n\",\n\t\t\t\toffset + size, handle->total_file_size);\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\treturn init_cpu_data(handle);\n}\n\nstatic int read_cpu_data(struct tracecmd_input *handle)\n{\n\tunsigned long long size;\n\tint cpus;\n\tint cpu;\n\n\t/*\n\t * Check if this is a latency report or not.\n\t */\n\tif (handle->file_state == TRACECMD_FILE_CPU_LATENCY)\n\t\treturn 1;\n\n\t/* We expect this to be flyrecord */\n\tif (handle->file_state != TRACECMD_FILE_CPU_FLYRECORD)\n\t\treturn -1;\n\n\tcpus = handle->cpus;\n\n\thandle->cpu_data = malloc(sizeof(*handle->cpu_data) * handle->cpus);\n\tif (!handle->cpu_data)\n\t\treturn -1;\n\tmemset(handle->cpu_data, 0, sizeof(*handle->cpu_data) * handle->cpus);\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\tunsigned long long offset;\n\n\t\thandle->cpu_data[cpu].cpu = cpu;\n\t\tread8(handle, &offset);\n\t\tread8(handle, &size);\n\t\thandle->cpu_data[cpu].file_offset = offset;\n\t\thandle->cpu_data[cpu].file_size = size;\n\t\tif (size && (offset + size > handle->total_file_size)) {\n\t\t\t/* this happens if the file got truncated */\n\t\t\tprintf(\"File possibly truncated. \"\n\t\t\t\t\"Need at least %llu, but file size is %zu.\\n\",\n\t\t\t\toffset + size, handle->total_file_size);\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t/*\n\t * It is possible that an option changed the number of CPUs.\n\t * If that happened, then there's \"empty\" cpu data saved for\n\t * backward compatibility.\n\t */\n\tif (cpus < handle->cpus) {\n\t\tunsigned long long ignore;\n\t\tint once = 0;\n\n\t\tread8(handle, &ignore); /* offset */\n\t\tread8(handle, &ignore); /* size */\n\t\tif (ignore != 0) {\n\t\t\tif (!once) {\n\t\t\t\ttracecmd_warning(\"ignored CPU data not zero size\");\n\t\t\t\tonce++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn init_cpu_data(handle);\n}\n\nstatic int read_data_and_size(struct tracecmd_input *handle,\n\t\t\t\t     char **data, unsigned long long *size)\n{\n\tif (read8(handle, size) < 0)\n\t\treturn -1;\n\t*data = malloc(*size + 1);\n\tif (!*data)\n\t\treturn -1;\n\tif (do_read_check(handle, *data, *size)) {\n\t\tfree(*data);\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nstatic int read_and_parse_cmdlines(struct tracecmd_input *handle)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tunsigned long long size;\n\tchar *cmdlines;\n\n\tif (CHECK_READ_STATE(handle, TRACECMD_FILE_CMD_LINES))\n\t\treturn 0;\n\n\tif (!HAS_SECTIONS(handle))\n\t\tsection_add_or_update(handle, TRACECMD_OPTION_CMDLINES, 0, 0,\n\t\t\t\t      lseek(handle->fd, 0, SEEK_CUR));\n\n\n\tif (read_data_and_size(handle, &cmdlines, &size) < 0)\n\t\treturn -1;\n\tcmdlines[size] = 0;\n\ttep_parse_saved_cmdlines(pevent, cmdlines);\n\tfree(cmdlines);\n\n\thandle->file_state = TRACECMD_FILE_CMD_LINES;\n\n\treturn 0;\n}\n\nstatic void extract_trace_clock(struct tracecmd_input *handle, char *line)\n{\n\tchar *clock = NULL;\n\tchar *next = NULL;\n\tchar *data;\n\n\tdata = strtok_r(line, \"[]\", &next);\n\tsscanf(data, \"%ms\", &clock);\n\t/* TODO: report if it fails to allocate */\n\thandle->trace_clock = clock;\n\n\tif (!clock)\n\t\treturn;\n\n\t/* Clear usecs if raw timestamps are requested */\n\tif (handle->flags & TRACECMD_FL_RAW_TS)\n\t\thandle->flags &= ~TRACECMD_FL_IN_USECS;\n\n\t/* tsc_calc is a conversion to nanoseconds */\n\tif (handle->tsc_calc.mult)\n\t\treturn;\n\n\t/* Clear usecs if not one of the specified clocks */\n\tif (strcmp(clock, \"local\") && strcmp(clock, \"global\") &&\n\t    strcmp(clock, \"uptime\") && strcmp(clock, \"perf\") &&\n\t    strncmp(clock, \"mono\", 4) && strcmp(clock, TSCNSEC_CLOCK) &&\n\t    strcmp(clock, \"tai\"))\n\t\thandle->flags &= ~TRACECMD_FL_IN_USECS;\n}\n\nvoid tracecmd_parse_trace_clock(struct tracecmd_input *handle,\n\t\t\t\tchar *file, int size __maybe_unused)\n{\n\tchar *line;\n\tchar *next = NULL;\n\n\tline = strtok_r(file, \" \", &next);\n\twhile (line) {\n\t\t/* current trace_clock is shown as \"[local]\". */\n\t\tif (*line == '[')\n\t\t\treturn extract_trace_clock(handle, line);\n\t\tline = strtok_r(NULL, \" \", &next);\n\t}\n}\n\nstatic int read_and_parse_trace_clock(struct tracecmd_input *handle,\n\t\t\t\t\t\t\tstruct tep_handle *pevent)\n{\n\tunsigned long long size;\n\tchar *trace_clock;\n\n\tif (read_data_and_size(handle, &trace_clock, &size) < 0)\n\t\treturn -1;\n\ttrace_clock[size] = 0;\n\ttracecmd_parse_trace_clock(handle, trace_clock, size);\n\tfree(trace_clock);\n\treturn 0;\n}\n\nstatic int init_data_v6(struct tracecmd_input *handle)\n{\n\tstruct tep_handle *pevent = handle->pevent;\n\tint ret;\n\n\tret = read_cpu_data(handle);\n\tif (ret < 0)\n\t\treturn ret;\n\n\tif (handle->use_trace_clock) {\n\t\t/*\n\t\t * There was a bug in the original setting of\n\t\t * the trace_clock file which let it get\n\t\t * corrupted. If it fails to read, force local\n\t\t * clock.\n\t\t */\n\t\tif (read_and_parse_trace_clock(handle, pevent) < 0) {\n\t\t\tchar clock[] = \"[local]\";\n\t\t\ttracecmd_warning(\"File has trace_clock bug, using local clock\");\n\t\t\ttracecmd_parse_trace_clock(handle, clock, 8);\n\t\t}\n\t}\n\treturn ret;\n}\n\nstatic int init_data(struct tracecmd_input *handle)\n{\n\treturn init_buffer_cpu_data(handle, &handle->top_buffer);\n}\n\n/**\n * tracecmd_init_data - prepare reading the data from trace.dat\n * @handle: input handle for the trace.dat file\n *\n * This prepares reading the data from trace.dat. This is called\n * after tracecmd_read_headers() and before tracecmd_read_data().\n */\nint tracecmd_init_data(struct tracecmd_input *handle)\n{\n\tint ret;\n\n\tif (!HAS_SECTIONS(handle))\n\t\tret = init_data_v6(handle);\n\telse\n\t\tret = init_data(handle);\n\ttracecmd_blk_hack(handle);\n\n\treturn ret;\n}\n\n/**\n * tracecmd_make_pipe - Have the handle read a pipe instead of a file\n * @handle: input handle to read from a pipe\n * @cpu: the cpu that the pipe represents\n * @fd: the read end of the pipe\n * @cpus: the total number of cpus for this handle\n *\n * In order to stream data from the binary trace files and produce\n * output or analyze the data, a tracecmd_input descriptor needs to\n * be created, and then converted into a form that can act on a\n * pipe.\n *\n * Note, there are limitations to what this descriptor can do.\n * Most notibly, it can not read backwards. Once a page is read\n * it can not be read at a later time (except if a record is attached\n * to it and is holding the page ref).\n *\n * It is expected that the handle has already been created and\n * tracecmd_read_headers() has run on it.\n */\nint tracecmd_make_pipe(struct tracecmd_input *handle, int cpu, int fd, int cpus)\n{\n\tenum kbuffer_long_size long_size;\n\tenum kbuffer_endian endian;\n\n\thandle->read_page = true;\n\thandle->use_pipe = true;\n\n\tif (!handle->cpus) {\n\t\thandle->cpus = cpus;\n\t\thandle->cpu_data = malloc(sizeof(*handle->cpu_data) * handle->cpus);\n\t\tif (!handle->cpu_data)\n\t\t\treturn -1;\n\t}\n\n\tif (cpu >= handle->cpus)\n\t\treturn -1;\n\n\n\tif (handle->long_size == 8)\n\t\tlong_size = KBUFFER_LSIZE_8;\n\telse\n\t\tlong_size = KBUFFER_LSIZE_4;\n\n\tif (tep_is_file_bigendian(handle->pevent))\n\t\tendian = KBUFFER_ENDIAN_BIG;\n\telse\n\t\tendian = KBUFFER_ENDIAN_LITTLE;\n\n\tmemset(&handle->cpu_data[cpu], 0, sizeof(handle->cpu_data[cpu]));\n\thandle->cpu_data[cpu].pipe_fd = fd;\n\thandle->cpu_data[cpu].cpu = cpu;\n\n\thandle->cpu_data[cpu].kbuf = kbuffer_alloc(long_size, endian);\n\tif (!handle->cpu_data[cpu].kbuf)\n\t\treturn -1;\n\tif (tep_is_old_format(handle->pevent))\n\t\tkbuffer_set_old_format(handle->cpu_data[cpu].kbuf);\n\n\thandle->cpu_data[cpu].file_offset = 0;\n\thandle->cpu_data[cpu].file_size = -1;\n\n\tinit_cpu(handle, cpu);\n\n\treturn 0;\n}\n\n/**\n * tracecmd_print_events - print the events that are stored in trace.dat\n * @handle: input handle for the trace.dat file\n * @regex: regex of events to print (NULL is all events)\n *\n * This is a debugging routine to print out the events that\n * are stored in a given trace.dat file.\n */\nvoid tracecmd_print_events(struct tracecmd_input *handle, const char *regex)\n{\n\tif (!regex)\n\t\tregex = \".*\";\n\n\tif (!HAS_SECTIONS(handle))\n\t\tread_headers_v6(handle, TRACECMD_FILE_ALL_EVENTS, regex);\n\n\tread_headers(handle, regex);\n}\n\n/* Show the cpu data stats */\nstatic void show_cpu_stats(struct tracecmd_input *handle)\n{\n\tstruct cpu_data *cpu_data;\n\tint i;\n\n\tfor (i = 0; i < handle->cpus; i++) {\n\t\tcpu_data = &handle->cpu_data[i];\n\t\tprintf(\"CPU%d data recorded at offset=0x%llx\\n\",\n\t\t       i, cpu_data->file_offset);\n\t\tprintf(\"    %lld bytes in size\\n\", cpu_data->file_size);\n\t}\n}\n\n/**\n * tracecmd_print_stats - prints the stats recorded in the options.\n * @handle: input handle for the trace.dat file\n *\n * Looks for the option TRACECMD_OPTION_CPUSTAT and prints out what's\n * stored there, if it is found. Otherwise it prints that none were found.\n */\nvoid tracecmd_print_stats(struct tracecmd_input *handle)\n{\n\tif (handle->cpustats)\n\t\tprintf(\"%s\\n\", handle->cpustats);\n\telse\n\t\tprintf(\" No stats in this file\\n\");\n\n\tshow_cpu_stats(handle);\n}\n\n/**\n * tracecmd_print_uname - prints the recorded uname if it was recorded\n * @handle: input handle for the trace.dat file\n *\n * Looks for the option TRACECMD_OPTION_UNAME and prints out what's\n * stored there, if it is found. Otherwise it prints that none were found.\n */\nvoid tracecmd_print_uname(struct tracecmd_input *handle)\n{\n\tif (handle->uname)\n\t\tprintf(\"%s\\n\", handle->uname);\n\telse\n\t\tprintf(\" uname was not recorded in this file\\n\");\n}\n\n/**\n * tracecmd_print_uname - prints the recorded uname if it was recorded\n * @handle: input handle for the trace.dat file\n *\n * Looks for the option TRACECMD_OPTION_VERSION and prints out what's\n * stored there, if it is found. Otherwise it prints that none were found.\n */\nvoid tracecmd_print_version(struct tracecmd_input *handle)\n{\n\tif (handle->version)\n\t\tprintf(\"%s\\n\", handle->version);\n\telse\n\t\tprintf(\" version was not recorded in this file\\n\");\n}\n\n/**\n * tracecmd_hooks - return the event hooks that were used in record\n * @handle: input handle for the trace.dat file\n *\n * If trace-cmd record used -H to save hooks, they are parsed and\n * presented as hooks here.\n *\n * Returns the hook list (do not free it, they are freed on close)\n */\nstruct hook_list *tracecmd_hooks(struct tracecmd_input *handle)\n{\n\treturn handle->hooks;\n}\n\nstatic int init_metadata_strings(struct tracecmd_input *handle, int size)\n{\n\tchar *tmp;\n\n\ttmp = realloc(handle->strings, handle->strings_size + size);\n\tif (!tmp)\n\t\treturn -1;\n\n\thandle->strings = tmp;\n\tif (do_read_check(handle, handle->strings + handle->strings_size, size))\n\t\treturn -1;\n\n\thandle->strings_size += size;\n\n\treturn 0;\n}\n\nstatic int read_metadata_strings(struct tracecmd_input *handle)\n{\n\tunsigned short flags;\n\tint found = 0;\n\tunsigned short id;\n\tunsigned int csize, rsize;\n\tunsigned long long size;\n\toff_t offset;\n\n\toffset = lseek(handle->fd, 0, SEEK_CUR);\n\tdo {\n\t\tif (read_section_header(handle, &id, &flags, &size, NULL))\n\t\t\tbreak;\n\t\tif (id == TRACECMD_OPTION_STRINGS) {\n\t\t\tfound++;\n\t\t\tif ((flags & TRACECMD_SEC_FL_COMPRESS)) {\n\t\t\t\tread4(handle, &csize);\n\t\t\t\tread4(handle, &rsize);\n\t\t\t\tdo_lseek(handle, -8, SEEK_CUR);\n\t\t\t\tif (tcmd_in_uncompress_block(handle))\n\t\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\trsize = size;\n\t\t\t}\n\t\t\tinit_metadata_strings(handle, rsize);\n\t\t\tif (flags & TRACECMD_SEC_FL_COMPRESS)\n\t\t\t\ttcmd_in_uncompress_reset(handle);\n\t\t} else {\n\t\t\tif (lseek(handle->fd, size, SEEK_CUR) == (off_t)-1)\n\t\t\t\tbreak;\n\t\t}\n\t} while (1);\n\n\tif (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\treturn found ? 0 : -1;\n}\n\n/**\n * tracecmd_alloc_fd - create a tracecmd_input handle from a file descriptor\n * @fd: the file descriptor for the trace.dat file\n * @flags: bitmask of enum tracecmd_open_flags\n *\n * Allocate a tracecmd_input handle from a file descriptor and open the\n * file. This tests if the file is of trace-cmd format and allocates\n * a parse event descriptor.\n *\n * The returned pointer is not ready to be read yet. A tracecmd_read_headers()\n * and tracecmd_init_data() still need to be called on the descriptor.\n *\n * Unless you know what you are doing with this, you want to use\n * tracecmd_open_fd() instead.\n */\nstruct tracecmd_input *tracecmd_alloc_fd(int fd, int flags)\n{\n\tstruct tracecmd_input *handle;\n\tchar test[] = TRACECMD_MAGIC;\n\tunsigned int page_size;\n\tsize_t offset;\n\tchar *version = NULL;\n\tchar *zver = NULL;\n\tchar *zname = NULL;\n\tchar buf[BUFSIZ];\n\tunsigned long ver;\n\n\thandle = malloc(sizeof(*handle));\n\tif (!handle)\n\t\treturn NULL;\n\tmemset(handle, 0, sizeof(*handle));\n\n\thandle->fd = fd;\n\thandle->ref = 1;\n\thandle->latz.fd = -1;\n\t/* By default, use usecs, unless told otherwise */\n\thandle->flags |= TRACECMD_FL_IN_USECS;\n\n#ifdef INMEMORY_DECOMPRESS\n\thandle->read_zpage = 1;\n#endif\n\tif (do_read_check(handle, buf, 3))\n\t\tgoto failed_read;\n\n\tif (memcmp(buf, test, 3) != 0)\n\t\tgoto failed_read;\n\n\tif (do_read_check(handle, buf, 7))\n\t\tgoto failed_read;\n\tif (memcmp(buf, \"tracing\", 7) != 0)\n\t\tgoto failed_read;\n\n\tversion = read_string(handle);\n\tif (!version)\n\t\tgoto failed_read;\n\ttracecmd_info(\"version = %s\", version);\n\tver = strtol(version, NULL, 10);\n\tif (!ver && errno)\n\t\tgoto failed_read;\n\tif (!tracecmd_is_version_supported(ver)) {\n\t\ttracecmd_warning(\"Unsupported file version %lu\", ver);\n\t\tgoto failed_read;\n\t}\n\thandle->file_version = ver;\n\tfree(version);\n\tversion = NULL;\n\n\tif (handle->file_version >= FILE_VERSION_SECTIONS)\n\t\thandle->flags |= TRACECMD_FL_SECTIONED;\n\tif (handle->file_version >= FILE_VERSION_COMPRESSION)\n\t\thandle->flags |= TRACECMD_FL_COMPRESSION;\n\n\tif (do_read_check(handle, buf, 1))\n\t\tgoto failed_read;\n\n\thandle->pevent = tep_alloc();\n\tif (!handle->pevent)\n\t\tgoto failed_read;\n\n\t/* register default ftrace functions first */\n\tif (!(flags & TRACECMD_FL_LOAD_NO_PLUGINS) &&\n\t    !(flags & TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS))\n\t\ttracecmd_ftrace_overrides(handle, &handle->finfo);\n\n\thandle->plugin_list = tcmd_load_plugins(handle->pevent, flags);\n\n\ttep_set_file_bigendian(handle->pevent, buf[0]);\n\n\tdo_read_check(handle, buf, 1);\n\thandle->long_size = buf[0];\n\ttep_set_long_size(handle->pevent, handle->long_size);\n\n\tread4(handle, &page_size);\n\thandle->page_size = page_size;\n\thandle->next_offset = page_size;\n\n\toffset = lseek(handle->fd, 0, SEEK_CUR);\n\thandle->total_file_size = lseek(handle->fd, 0, SEEK_END);\n\tlseek(handle->fd, offset, SEEK_SET);\n\n\tif (HAS_COMPRESSION(handle)) {\n\t\tzname = read_string(handle);\n\t\tif (!zname)\n\t\t\tgoto failed_read;\n\n\t\tzver = read_string(handle);\n\t\tif (!zver)\n\t\t\tgoto failed_read;\n\n\t\tif (strcmp(zname, \"none\") == 0) {\n\t\t\thandle->read_zpage = false;\n\t\t\thandle->flags &= ~TRACECMD_FL_COMPRESSION;\n\t\t} else {\n\t\t\thandle->compress = tracecmd_compress_alloc(zname, zver,\n\t\t\t\t\t\t\t\t   handle->fd,\n\t\t\t\t\t\t\t\t   handle->pevent, NULL);\n\t\t\tif (!handle->compress) {\n\t\t\t\ttracecmd_warning(\"Unsupported file compression %s %s\", zname, zver);\n\t\t\t\tgoto failed_read;\n\t\t\t}\n\t\t}\n\n\t\tfree(zname);\n\t\tfree(zver);\n\t}\n\n\tif (HAS_SECTIONS(handle)) {\n\t\tif (read8(handle, &(handle->options_start))) {\n\t\t\ttracecmd_warning(\"Filed to read the offset of the first option section\");\n\t\t\tgoto failed_read;\n\t\t}\n\t\tread_metadata_strings(handle);\n\t}\n\n\thandle->file_state = TRACECMD_FILE_INIT;\n\n\treturn handle;\n\n failed_read:\n\tfree(version);\n\tfree(zname);\n\tfree(zver);\n\tfree(handle);\n\n\treturn NULL;\n}\n\n/**\n * tracecmd_alloc_fd - create a tracecmd_input handle from a file name\n * @file: the file name of the file that is of tracecmd data type.\n * @flags: bitmask of enum tracecmd_open_flags\n *\n * Allocate a tracecmd_input handle from a given file name and open the\n * file. This tests if the file is of trace-cmd format and allocates\n * a parse event descriptor.\n *\n * The returned pointer is not ready to be read yet. A tracecmd_read_headers()\n * and tracecmd_init_data() still need to be called on the descriptor.\n *\n * Unless you know what you are doing with this, you want to use\n * tracecmd_open() instead.\n */\nstruct tracecmd_input *tracecmd_alloc(const char *file, int flags)\n{\n\tint fd;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0)\n\t\treturn NULL;\n\n\treturn tracecmd_alloc_fd(fd, flags);\n}\n\n/**\n * tracecmd_open_fd - create a tracecmd_handle from the trace.dat file descriptor\n * @fd: the file descriptor for the trace.dat file\n * @flags: bitmask of enum tracecmd_open_flags\n */\nstruct tracecmd_input *tracecmd_open_fd(int fd, int flags)\n{\n\tstruct tracecmd_input *handle;\n\tint ret;\n\n\thandle = tracecmd_alloc_fd(fd, flags);\n\tif (!handle)\n\t\treturn NULL;\n\n\tif (tracecmd_read_headers(handle, 0) < 0)\n\t\tgoto fail;\n\n\tif ((ret = tracecmd_init_data(handle)) < 0)\n\t\tgoto fail;\n\n\treturn handle;\n\nfail:\n\ttracecmd_close(handle);\n\treturn NULL;\n}\n\n/**\n * tracecmd_open - create a tracecmd_handle from a given file\n * @file: the file name of the file that is of tracecmd data type.\n * @flags: bitmask of enum tracecmd_open_flags\n */\nstruct tracecmd_input *tracecmd_open(const char *file, int flags)\n{\n\tint fd;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0)\n\t\treturn NULL;\n\n\treturn tracecmd_open_fd(fd, flags);\n}\n\n/**\n * tracecmd_open_head - create a tracecmd_handle from a given file, read\n *\t\t\tand parse only the trace headers from the file\n * @file: the file name of the file that is of tracecmd data type.\n * @flags: bitmask of enum tracecmd_open_flags\n */\nstruct tracecmd_input *tracecmd_open_head(const char *file, int flags)\n{\n\tstruct tracecmd_input *handle;\n\tint fd;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0)\n\t\treturn NULL;\n\n\thandle = tracecmd_alloc_fd(fd, flags);\n\tif (!handle)\n\t\treturn NULL;\n\n\tif (tracecmd_read_headers(handle, 0) < 0)\n\t\tgoto fail;\n\n\treturn handle;\n\nfail:\n\ttracecmd_close(handle);\n\treturn NULL;\n}\n\n/**\n * tracecmd_ref - add a reference to the handle\n * @handle: input handle for the trace.dat file\n *\n * Some applications may share a handle between parts of\n * the application. Let those parts add reference counters\n * to the handle, and the last one to close it will free it.\n */\nvoid tracecmd_ref(struct tracecmd_input *handle)\n{\n\tif (!handle)\n\t\treturn;\n\n\thandle->ref++;\n}\n\nstatic inline void free_buffer(struct input_buffer_instance *buf)\n{\n\tfree(buf->name);\n\tfree(buf->clock);\n\tfree(buf->cpu_data);\n}\n\n/**\n * tracecmd_close - close and free the trace.dat handle\n * @handle: input handle for the trace.dat file\n *\n * Close the file descriptor of the handle and frees\n * the resources allocated by the handle.\n */\nvoid tracecmd_close(struct tracecmd_input *handle)\n{\n\tstruct zchunk_cache *cache;\n\tstruct file_section *del_sec;\n\tstruct cpu_data *cpu_data;\n\tstruct page_map *page_map, *n;\n\tint cpu;\n\tint i;\n\n\tif (!handle)\n\t\treturn;\n\n\tif (handle->ref <= 0) {\n\t\ttracecmd_warning(\"tracecmd: bad ref count on handle\");\n\t\treturn;\n\t}\n\n\tif (--handle->ref)\n\t\treturn;\n\n\tfor (cpu = 0; cpu < handle->cpus; cpu++) {\n\t\t/* The tracecmd_peek_data may have cached a record */\n\t\tfree_next(handle, cpu);\n\t\tfree_page(handle, cpu);\n\t\tif (handle->cpu_data) {\n\t\t\tcpu_data = &handle->cpu_data[cpu];\n\t\t\tif (cpu_data->kbuf) {\n\t\t\t\tkbuffer_free(cpu_data->kbuf);\n\t\t\t\tif (cpu_data->page_map)\n\t\t\t\t\tfree_page_map(cpu_data->page_map);\n\n\t\t\t\tif (cpu_data->page_cnt)\n\t\t\t\t\ttracecmd_warning(\"%d pages still allocated on cpu %d%s\",\n\t\t\t\t\t\t\t cpu_data->page_cnt, cpu,\n\t\t\t\t\t\t\t show_records(cpu_data->pages,\n\t\t\t\t\t\t\t\t      cpu_data->nr_pages));\n\t\t\t\tfree(cpu_data->pages);\n\t\t\t}\n\t\t\tif (cpu_data->compress.fd >= 0) {\n\t\t\t\tclose(cpu_data->compress.fd);\n\t\t\t\tunlink(cpu_data->compress.file);\n\t\t\t}\n\t\t\twhile (cpu_data->compress.cache.node) {\n\t\t\t\tstruct trace_rbtree_node *node;\n\t\t\t\tnode = trace_rbtree_pop_nobalance(&cpu_data->compress.cache);\n\t\t\t\tcache = container_of(node, struct zchunk_cache, node);\n\t\t\t\tfree(cache->map);\n\t\t\t\tfree(cache);\n\t\t\t}\n\t\t\tfree(cpu_data->compress.chunks);\n\t\t\tlist_for_each_entry_safe(page_map, n, &cpu_data->page_maps, list) {\n\t\t\t\tlist_del(&page_map->list);\n\t\t\t\tfree(page_map);\n\t\t\t}\n\t\t}\n\t}\n\n\tfree(handle->cpustats);\n\tfree(handle->cpu_data);\n\tfree(handle->uname);\n\tfree(handle->trace_clock);\n\tfree(handle->strings);\n\tfree(handle->version);\n\tfree(handle->followers);\n\tfree(handle->missed_followers);\n\ttcmd_guest_map_free(handle->map);\n\tclose(handle->fd);\n\tfree(handle->latz.chunks);\n\tif (handle->latz.fd >= 0) {\n\t\tclose(handle->latz.fd);\n\t\tunlink(handle->latz.file);\n\t}\n\twhile (handle->sections) {\n\t\tdel_sec = handle->sections;\n\t\thandle->sections = handle->sections->next;\n\t\tfree(del_sec);\n\t}\n\n\tfree_buffer(&handle->top_buffer);\n\tfor (i = 0; i < handle->nr_buffers; i++)\n\t\tfree_buffer(&handle->buffers[i]);\n\tfree(handle->buffers);\n\n\ttracecmd_free_hooks(handle->hooks);\n\thandle->hooks = NULL;\n\n\ttrace_pid_map_free(handle->pid_maps);\n\thandle->pid_maps = NULL;\n\n\ttrace_tsync_offset_free(&handle->host);\n\ttrace_guests_free(handle);\n\n\ttcmd_filter_free(handle->filter);\n\n\tif (handle->flags & TRACECMD_FL_BUFFER_INSTANCE)\n\t\ttracecmd_close(handle->parent);\n\telse {\n\t\t/* Only main handle frees plugins, pevent and compression context */\n\t\ttracecmd_compress_destroy(handle->compress);\n\t\ttep_unload_plugins(handle->plugin_list, handle->pevent);\n\t\ttep_free(handle->pevent);\n\t}\n\tfree(handle);\n}\n\nstatic int read_copy_size8(struct tracecmd_input *in_handle,\n\t\t\t   struct tracecmd_output *out_handle, unsigned long long *size)\n{\n\t/* read size */\n\tif (do_read_check(in_handle, size, 8))\n\t\treturn -1;\n\n\tif (tcmd_do_write_check(out_handle, size, 8))\n\t\treturn -1;\n\n\t*size = tep_read_number(in_handle->pevent, size, 8);\n\treturn 0;\n}\n\nstatic int read_copy_size4(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle,\n\t\t\t   unsigned int *size)\n{\n\t/* read size */\n\tif (do_read_check(in_handle, size, 4))\n\t\treturn -1;\n\n\tif (tcmd_do_write_check(out_handle, size, 4))\n\t\treturn -1;\n\n\t*size = tep_read_number(in_handle->pevent, size, 4);\n\treturn 0;\n}\n\nstatic int read_copy_data(struct tracecmd_input *in_handle,\n\t\t\t  unsigned long long size,\n\t\t\t  struct tracecmd_output *out_handle)\n{\n\tchar *buf;\n\n\tbuf = malloc(size);\n\tif (!buf)\n\t\treturn -1;\n\tif (do_read_check(in_handle, buf, size))\n\t\tgoto failed_read;\n\n\tif (tcmd_do_write_check(out_handle, buf, size))\n\t\tgoto failed_read;\n\t\n\tfree(buf);\n\n\treturn 0;\n\n failed_read:\n\tfree(buf);\n\treturn -1;\n}\n\n\nstatic bool check_in_state(struct tracecmd_input *handle, int new_state)\n{\n\treturn tcmd_check_file_state(handle->file_version, handle->file_state, new_state);\n}\n\nstatic int copy_header_files(struct tracecmd_input *in_handle,\n\t\t\t     struct tracecmd_output *out_handle)\n{\n\tstruct file_section *sec;\n\tunsigned long long offset;\n\tunsigned long long size;\n\n\tif (!check_in_state(in_handle, TRACECMD_FILE_HEADERS) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_HEADERS))\n\t\treturn -1;\n\n\tsec = section_open(in_handle, TRACECMD_OPTION_HEADER_INFO);\n\tif (!sec)\n\t\treturn -1;\n\n\toffset = tcmd_out_write_section_header(out_handle, TRACECMD_OPTION_HEADER_INFO,\n\t\t\t\t\t       \"headers\", TRACECMD_SEC_FL_COMPRESS, true);\n\ttcmd_out_compression_start(out_handle);\n\n\t/* \"header_page\"  */\n\tif (read_copy_data(in_handle, 12, out_handle) < 0)\n\t\tgoto error;\n\n\tif (read_copy_size8(in_handle, out_handle, &size) < 0)\n\t\tgoto error;\n\n\tif (read_copy_data(in_handle, size, out_handle) < 0)\n\t\tgoto error;\n\n\t/* \"header_event\"  */\n\tif (read_copy_data(in_handle, 13, out_handle) < 0)\n\t\tgoto error;\n\n\tif (read_copy_size8(in_handle, out_handle, &size) < 0)\n\t\tgoto error;\n\n\tif (read_copy_data(in_handle, size, out_handle) < 0)\n\t\tgoto error;\n\n\tin_handle->file_state = TRACECMD_FILE_HEADERS;\n\tif (tcmd_out_compression_end(out_handle))\n\t\tgoto error;\n\n\ttcmd_out_set_file_state(out_handle, in_handle->file_state);\n\tsection_close(in_handle, sec);\n\n\tif (tcmd_out_update_section_header(out_handle, offset))\n\t\tgoto error;\n\n\treturn 0;\nerror:\n\ttcmd_out_compression_reset(out_handle);\n\tsection_close(in_handle, sec);\n\treturn -1;\n}\n\nstatic int copy_ftrace_files(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)\n{\n\tstruct file_section *sec;\n\tunsigned long long offset;\n\tunsigned long long size;\n\tunsigned int count;\n\tunsigned int i;\n\n\tif (!check_in_state(in_handle, TRACECMD_FILE_FTRACE_EVENTS) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_FTRACE_EVENTS))\n\t\treturn -1;\n\n\tsec = section_open(in_handle, TRACECMD_OPTION_FTRACE_EVENTS);\n\tif (!sec)\n\t\treturn -1;\n\toffset = tcmd_out_write_section_header(out_handle, TRACECMD_OPTION_FTRACE_EVENTS,\n\t\t\t\t\t       \"ftrace events\", TRACECMD_SEC_FL_COMPRESS, true);\n\n\ttcmd_out_compression_start(out_handle);\n\n\tif (read_copy_size4(in_handle, out_handle, &count) < 0)\n\t\tgoto error;\n\n\tfor (i = 0; i < count; i++) {\n\n\t\tif (read_copy_size8(in_handle, out_handle, &size) < 0)\n\t\t\tgoto error;\n\n\t\tif (read_copy_data(in_handle, size, out_handle) < 0)\n\t\t\tgoto error;\n\t}\n\n\tin_handle->file_state = TRACECMD_FILE_FTRACE_EVENTS;\n\tif (tcmd_out_compression_end(out_handle))\n\t\tgoto error;\n\n\ttcmd_out_set_file_state(out_handle, in_handle->file_state);\n\n\tsection_close(in_handle, sec);\n\n\tif (tcmd_out_update_section_header(out_handle, offset))\n\t\tgoto error;\n\n\treturn 0;\nerror:\n\ttcmd_out_compression_reset(out_handle);\n\tsection_close(in_handle, sec);\n\treturn -1;\n}\n\nstatic int copy_event_files(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)\n{\n\tstruct file_section *sec;\n\tunsigned long long offset;\n\tunsigned long long size;\n\tchar *system;\n\tunsigned int systems;\n\tunsigned int count;\n\tunsigned int i,x;\n\n\tif (!check_in_state(in_handle, TRACECMD_FILE_ALL_EVENTS) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_ALL_EVENTS))\n\t\treturn -1;\n\n\tsec = section_open(in_handle, TRACECMD_OPTION_EVENT_FORMATS);\n\tif (!sec)\n\t\treturn -1;\n\toffset = tcmd_out_write_section_header(out_handle, TRACECMD_OPTION_EVENT_FORMATS,\n\t\t\t\t\t       \"events format\", TRACECMD_SEC_FL_COMPRESS, true);\n\n\ttcmd_out_compression_start(out_handle);\n\n\tif (read_copy_size4(in_handle, out_handle, &systems) < 0)\n\t\tgoto error;\n\n\tfor (i = 0; i < systems; i++) {\n\t\tsystem = read_string(in_handle);\n\t\tif (!system)\n\t\t\tgoto error;\n\t\tif (tcmd_do_write_check(out_handle, system, strlen(system) + 1)) {\n\t\t\tfree(system);\n\t\t\tgoto error;\n\t\t}\n\t\tfree(system);\n\n\t\tif (read_copy_size4(in_handle, out_handle, &count) < 0)\n\t\t\tgoto error;\n\n\t\tfor (x=0; x < count; x++) {\n\t\t\tif (read_copy_size8(in_handle, out_handle, &size) < 0)\n\t\t\t\tgoto error;\n\n\t\t\tif (read_copy_data(in_handle, size, out_handle) < 0)\n\t\t\t\tgoto error;\n\t\t}\n\t}\n\n\tin_handle->file_state = TRACECMD_FILE_ALL_EVENTS;\n\tif (tcmd_out_compression_end(out_handle))\n\t\tgoto error;\n\n\ttcmd_out_set_file_state(out_handle, in_handle->file_state);\n\n\tsection_close(in_handle, sec);\n\n\tif (tcmd_out_update_section_header(out_handle, offset))\n\t\tgoto error;\n\n\treturn 0;\nerror:\n\ttcmd_out_compression_reset(out_handle);\n\tsection_close(in_handle, sec);\n\treturn -1;\n}\n\nstatic int copy_proc_kallsyms(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)\n{\n\tstruct file_section *sec;\n\tunsigned long long offset;\n\tunsigned int size;\n\n\tif (!check_in_state(in_handle, TRACECMD_FILE_KALLSYMS) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_KALLSYMS))\n\t\treturn -1;\n\n\tsec = section_open(in_handle, TRACECMD_OPTION_KALLSYMS);\n\tif (!sec)\n\t\treturn -1;\n\toffset = tcmd_out_write_section_header(out_handle, TRACECMD_OPTION_KALLSYMS,\n\t\t\t\t\t  \"kallsyms\", TRACECMD_SEC_FL_COMPRESS, true);\n\n\ttcmd_out_compression_start(out_handle);\n\tif (read_copy_size4(in_handle, out_handle, &size) < 0)\n\t\tgoto error;\n\n\tif (!size)\n\t\tgoto out; /* OK? */\n\n\tif (read_copy_data(in_handle, size, out_handle) < 0)\n\t\tgoto error;\nout:\n\tin_handle->file_state = TRACECMD_FILE_KALLSYMS;\n\tif (tcmd_out_compression_end(out_handle))\n\t\tgoto error;\n\n\ttcmd_out_set_file_state(out_handle, in_handle->file_state);\n\n\tsection_close(in_handle, sec);\n\n\tif (tcmd_out_update_section_header(out_handle, offset))\n\t\tgoto error;\n\n\treturn 0;\nerror:\n\ttcmd_out_compression_reset(out_handle);\n\tsection_close(in_handle, sec);\n\treturn -1;\n}\n\nstatic int copy_ftrace_printk(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)\n{\n\tstruct file_section *sec;\n\tunsigned long long offset;\n\tunsigned int size;\n\n\tif (!check_in_state(in_handle, TRACECMD_FILE_PRINTK) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_PRINTK))\n\t\treturn -1;\n\n\tsec = section_open(in_handle, TRACECMD_OPTION_PRINTK);\n\tif (!sec)\n\t\treturn -1;\n\n\toffset = tcmd_out_write_section_header(out_handle, TRACECMD_OPTION_PRINTK,\n\t\t\t\t\t  \"printk\", TRACECMD_SEC_FL_COMPRESS, true);\n\n\ttcmd_out_compression_start(out_handle);\n\n\tif (read_copy_size4(in_handle, out_handle, &size) < 0)\n\t\tgoto error;\n\n\tif (!size)\n\t\tgoto out; /* OK? */\n\n\tif (read_copy_data(in_handle, size, out_handle) < 0)\n\t\tgoto error;\n\nout:\n\tin_handle->file_state = TRACECMD_FILE_PRINTK;\n\tif (tcmd_out_compression_end(out_handle))\n\t\tgoto error;\n\n\ttcmd_out_set_file_state(out_handle, in_handle->file_state);\n\n\tsection_close(in_handle, sec);\n\n\tif (tcmd_out_update_section_header(out_handle, offset))\n\t\tgoto error;\n\n\treturn 0;\nerror:\n\ttcmd_out_compression_reset(out_handle);\n\tsection_close(in_handle, sec);\n\treturn -1;\n}\n\nstatic int copy_command_lines(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)\n{\n\tstruct file_section *sec;\n\tunsigned long long offset;\n\tunsigned long long size;\n\n\tif (!check_in_state(in_handle, TRACECMD_FILE_CMD_LINES) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_CMD_LINES))\n\t\treturn -1;\n\n\tsec = section_open(in_handle, TRACECMD_OPTION_CMDLINES);\n\tif (!sec)\n\t\treturn -1;\n\toffset = tcmd_out_write_section_header(out_handle, TRACECMD_OPTION_CMDLINES,\n\t\t\t\t\t       \"command lines\", TRACECMD_SEC_FL_COMPRESS, true);\n\n\ttcmd_out_compression_start(out_handle);\n\n\tif (read_copy_size8(in_handle, out_handle, &size) < 0)\n\t\tgoto error;\n\n\tif (!size)\n\t\tgoto out; /* OK? */\n\n\tif (read_copy_data(in_handle, size, out_handle) < 0)\n\t\tgoto error;\n\nout:\n\tin_handle->file_state = TRACECMD_FILE_CMD_LINES;\n\tif (tcmd_out_compression_end(out_handle))\n\t\tgoto error;\n\n\ttcmd_out_set_file_state(out_handle, in_handle->file_state);\n\n\tsection_close(in_handle, sec);\n\n\tif (tcmd_out_update_section_header(out_handle, offset))\n\t\tgoto error;\n\n\treturn 0;\nerror:\n\ttcmd_out_compression_reset(out_handle);\n\tsection_close(in_handle, sec);\n\treturn -1;\n}\n\nstatic int copy_cpu_count(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)\n{\n\tunsigned int cpus;\n\n\tif (!check_in_state(in_handle, TRACECMD_FILE_CPU_COUNT) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_CPU_COUNT))\n\t\treturn -1;\n\n\tif (!HAS_SECTIONS(in_handle)) {\n\t\tif (read4(in_handle, &cpus))\n\t\t\treturn -1;\n\t} else {\n\t\tcpus = in_handle->max_cpu;\n\t}\n\n\tif (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS) {\n\t\tcpus = tep_read_number(in_handle->pevent, &cpus, 4);\n\t\tif (tcmd_do_write_check(out_handle, &cpus, 4))\n\t\t\treturn -1;\n\t} else {\n\t\ttracecmd_add_option(out_handle, TRACECMD_OPTION_CPUCOUNT, sizeof(int), &cpus);\n\t}\n\n\tin_handle->file_state = TRACECMD_FILE_CPU_COUNT;\n\ttcmd_out_set_file_state(out_handle, in_handle->file_state);\n\n\treturn 0;\n}\n\n/**\n * tracecmd_copy_headers - Copy headers from a tracecmd_input handle to a file descriptor\n * @in_handle: input handle for the trace.dat file to copy from.\n * @out_handle: output handle to the trace.dat file to copy to.\n * @start_state: The file state to start copying from (zero for the beginnig)\n * @end_state: The file state to stop at (zero for up to cmdlines)\n *\n * This is used to copy trace header data of a trace.dat file to a\n * file descriptor. Using @start_state and @end_state it may be used\n * multiple times against the input handle.\n *\n * NOTE: The input handle is also modified, and ends at the end\n *       state as well.\n */\nint tracecmd_copy_headers(struct tracecmd_input *in_handle,\n\t\t\t  struct tracecmd_output *out_handle,\n\t\t\t  enum tracecmd_file_states start_state,\n\t\t\t  enum tracecmd_file_states end_state)\n{\n\tstruct file_section *sec = NULL;\n\tint ret;\n\n\tif (!start_state)\n\t\tstart_state = TRACECMD_FILE_HEADERS;\n\tif (!end_state)\n\t\tend_state = TRACECMD_FILE_CMD_LINES;\n\n\tif (start_state > end_state)\n\t\treturn -1;\n\n\tif (end_state < TRACECMD_FILE_HEADERS)\n\t\treturn 0;\n\n\tif (in_handle->file_state >= start_state) {\n\t\t/* Set the handle to just before the start state */\n\t\tsec = section_open(in_handle, TRACECMD_OPTION_HEADER_INFO);\n\t\tif (!sec)\n\t\t\treturn -1;\n\t\t/* Now that the file handle has moved, change its state */\n\t\tin_handle->file_state = TRACECMD_FILE_INIT;\n\t}\n\n\t/* Try to bring the input up to the start state - 1 */\n\tret = tracecmd_read_headers(in_handle, start_state - 1);\n\tif (sec)\n\t\tsection_close(in_handle, sec);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tswitch (start_state) {\n\tcase TRACECMD_FILE_HEADERS:\n\t\tret = copy_header_files(in_handle, out_handle);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\n\t\t/* fallthrough */\n\tcase TRACECMD_FILE_FTRACE_EVENTS:\n\t\t/* handle's state is now updating with the copies */\n\t\tif (end_state <= in_handle->file_state)\n\t\t\treturn 0;\n\n\t\tret = copy_ftrace_files(in_handle, out_handle);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\n\t\t/* fallthrough */\n\tcase TRACECMD_FILE_ALL_EVENTS:\n\t\tif (end_state <= in_handle->file_state)\n\t\t\treturn 0;\n\n\t\tret = copy_event_files(in_handle, out_handle);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\n\t\t/* fallthrough */\n\tcase TRACECMD_FILE_KALLSYMS:\n\t\tif (end_state <= in_handle->file_state)\n\t\t\treturn 0;\n\n\t\tret = copy_proc_kallsyms(in_handle, out_handle);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\n\t\t/* fallthrough */\n\tcase TRACECMD_FILE_PRINTK:\n\t\tif (end_state <= in_handle->file_state)\n\t\t\treturn 0;\n\n\t\tret = copy_ftrace_printk(in_handle, out_handle);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\n\t\t/* fallthrough */\n\tcase TRACECMD_FILE_CMD_LINES:\n\t\tif (end_state <= in_handle->file_state)\n\t\t\treturn 0;\n\n\t\t/* Optional */\n\t\tcopy_command_lines(in_handle, out_handle);\n\n\t\t/* fallthrough */\n\tcase TRACECMD_FILE_CPU_COUNT:\n\t\tif (end_state <= in_handle->file_state)\n\t\t\treturn 0;\n\n\t\tret = copy_cpu_count(in_handle, out_handle);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\n\t\t/* fallthrough */\n\tdefault:\n\t\tbreak;\n\t}\n\n out:\n\treturn ret < 0 ? -1 : 0;\n}\n\nint tracecmd_copy_buffer_descr(struct tracecmd_input *in_handle,\n\t\t\t       struct tracecmd_output *out_handle)\n{\n\tint i;\n\n\tif (tracecmd_get_out_file_version(out_handle) >= FILE_VERSION_SECTIONS)\n\t\treturn 0;\n\n\tfor (i = 0; i < in_handle->nr_buffers; i++)\n\t\ttracecmd_add_buffer_info(out_handle, in_handle->buffers[i].name, 0);\n\n\treturn tracecmd_write_buffer_info(out_handle);\n}\n\nstatic int copy_options_recursive(struct tracecmd_input *in_handle,\n\t\t\t\t  struct tracecmd_output *out_handle)\n{\n\tunsigned short id, flags = 0;\n\tunsigned short option, en2;\n\tunsigned long long next;\n\tunsigned int size, en4;\n\tbool skip;\n\n\tfor (;;) {\n\t\tif (do_read_check(in_handle, &option, 2))\n\t\t\treturn -1;\n\n\t\ten2 = tep_read_number(in_handle->pevent, &option, 2);\n\n\t\tif (en2 == TRACECMD_OPTION_DONE && !HAS_SECTIONS(in_handle))\n\t\t\treturn 0;\n\n\t\t/* next 4 bytes is the size of the option */\n\t\tif (do_read_check(in_handle, &size, 4))\n\t\t\treturn -1;\n\n\t\ten4 = tep_read_number(in_handle->pevent, &size, 4);\n\t\tif (en2 == TRACECMD_OPTION_DONE) {\n\t\t\t/* option done v7 */\n\t\t\tif (en4 < 8)\n\t\t\t\treturn -1;\n\n\t\t\tif (read8(in_handle, &next))\n\t\t\t\treturn -1;\n\n\t\t\tif (!next)\n\t\t\t\tbreak;\n\n\t\t\tif (do_lseek(in_handle, next, SEEK_SET) == (off_t)-1)\n\t\t\t\treturn -1;\n\n\t\t\tif (read_section_header(in_handle, &id, &flags, NULL, NULL))\n\t\t\t\treturn -1;\n\n\t\t\tif (id != TRACECMD_OPTION_DONE)\n\t\t\t\treturn -1;\n\n\t\t\tif (flags & TRACECMD_SEC_FL_COMPRESS && tcmd_in_uncompress_block(in_handle))\n\t\t\t\treturn -1;\n\n\t\t\treturn copy_options_recursive(in_handle, out_handle);\n\t\t}\n\t\t/* Do not copy these, as they have file specific offsets */\n\t\tswitch (en2) {\n\t\tcase TRACECMD_OPTION_BUFFER:\n\t\tcase TRACECMD_OPTION_BUFFER_TEXT:\n\t\tcase TRACECMD_OPTION_HEADER_INFO:\n\t\tcase TRACECMD_OPTION_FTRACE_EVENTS:\n\t\tcase TRACECMD_OPTION_EVENT_FORMATS:\n\t\tcase TRACECMD_OPTION_KALLSYMS:\n\t\tcase TRACECMD_OPTION_PRINTK:\n\t\tcase TRACECMD_OPTION_CMDLINES:\n\t\t\tskip = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tskip = false;\n\t\t\tbreak;\n\t\t}\n\t\tif (skip) {\n\t\t\tdo_lseek(in_handle, en4, SEEK_CUR);\n\t\t\tcontinue;\n\t\t}\n\t\tif (tcmd_do_write_check(out_handle, &option, 2))\n\t\t\treturn -1;\n\n\t\tif (tcmd_do_write_check(out_handle, &size, 4))\n\t\t\treturn -1;\n\n\t\tif (read_copy_data(in_handle, en4, out_handle))\n\t\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nstatic int copy_options(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)\n{\n\tunsigned long long offset, start;\n\tunsigned short id, en2, flags = 0;\n\tint tmp;\n\n\tif (HAS_SECTIONS(in_handle)) {\n\t\tif (read_section_header(in_handle, &id, &flags, NULL, NULL))\n\t\t\treturn -1;\n\n\t\tif (id != TRACECMD_OPTION_DONE)\n\t\t\treturn -1;\n\n\t\tif (flags & TRACECMD_SEC_FL_COMPRESS && tcmd_in_uncompress_block(in_handle))\n\t\t\treturn -1;\n\t}\n\tstart = tracecmd_get_out_file_offset(out_handle);\n\tif (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS) {\n\t\tif (tcmd_do_write_check(out_handle, \"options  \", 10))\n\t\t\treturn -1;\n\t}\n\n\toffset = tcmd_out_write_section_header(out_handle, TRACECMD_OPTION_DONE, \"options\", 0, false);\n\n\tif (copy_options_recursive(in_handle, out_handle))\n\t\tgoto error;\n\n\tid = TRACECMD_OPTION_DONE;\n\ten2 = tep_read_number(in_handle->pevent, &id, 2);\n\tif (tcmd_do_write_check(out_handle, &en2, 2))\n\t\tgoto error;\n\n\tif (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS) {\n\t\ttcmd_out_save_options_offset(out_handle, start);\n\t} else {\n\t\ttmp = 8;\n\t\tif (tcmd_do_write_check(out_handle, &tmp, 4))\n\t\t\tgoto error;\n\n\t\ttcmd_out_save_options_offset(out_handle, start);\n\t\tstart = 0;\n\t\tif (tcmd_do_write_check(out_handle, &start, 8))\n\t\t\tgoto error;\n\t}\n\ttcmd_out_update_section_header(out_handle, offset);\n\tif (flags & TRACECMD_SEC_FL_COMPRESS)\n\t\ttcmd_in_uncompress_reset(in_handle);\n\tin_handle->file_state = TRACECMD_FILE_OPTIONS;\n\ttcmd_out_set_file_state(out_handle, in_handle->file_state);\n\t/* Append local options */\n\treturn tracecmd_append_options(out_handle);\n\nerror:\n\tif (flags & TRACECMD_SEC_FL_COMPRESS)\n\t\ttcmd_in_uncompress_reset(in_handle);\n\treturn 0;\n}\n\nint tracecmd_copy_options(struct tracecmd_input *in_handle,\n\t\t\t  struct tracecmd_output *out_handle)\n{\n\tif (!check_in_state(in_handle, TRACECMD_FILE_OPTIONS) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_OPTIONS))\n\t\treturn -1;\n\n\tif (!in_handle->options_start)\n\t\treturn 0;\n\n\tif (lseek(in_handle->fd, in_handle->options_start, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\tif (copy_options(in_handle, out_handle) < 0)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic int copy_trace_latency(struct tracecmd_input *in_handle,\n\t\t\t      struct tracecmd_output *out_handle, const char *buf_name)\n{\n\tint page_size = getpagesize();\n\tunsigned long long wsize;\n\tunsigned long long offset;\n\tint fd;\n\n\tif (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS &&\n\t    tcmd_do_write_check(out_handle, \"latency  \", 10))\n\t\treturn -1;\n\n\toffset = tracecmd_get_out_file_offset(out_handle);\n\n\tif (tracecmd_get_out_file_version(out_handle) >= FILE_VERSION_SECTIONS &&\n\t    !tcmd_out_add_buffer_option(out_handle, buf_name, TRACECMD_OPTION_BUFFER_TEXT,\n\t\t\t\t   offset, 0, NULL, page_size))\n\t\treturn -1;\n\n\toffset = tcmd_out_write_section_header(out_handle, TRACECMD_OPTION_BUFFER_TEXT,\n\t\t\t\t\t  \"buffer latency\", TRACECMD_SEC_FL_COMPRESS, false);\n\n\tif (in_handle->latz.fd >= 0)\n\t\tfd = in_handle->latz.fd;\n\telse\n\t\tfd = in_handle->fd;\n\n\tif (!tcmd_out_copy_fd_compress(out_handle, fd, 0, &wsize, page_size))\n\t\treturn -1;\n\n\tif (tcmd_out_update_section_header(out_handle, offset))\n\t\treturn -1;\n\n\ttcmd_out_set_file_state(out_handle, TRACECMD_FILE_CPU_LATENCY);\n\treturn 0;\n}\n\nstatic int copy_trace_flyrecord_data(struct tracecmd_input *in_handle,\n\t\t\t\t     struct tracecmd_output *out_handle, const char *buff_name)\n{\n\tstruct cpu_data_source *data;\n\tint total_size = 0;\n\tint cpus;\n\tint ret;\n\tint i, j;\n\n\tif (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS)\n\t\tcpus = in_handle->max_cpu;\n\telse\n\t\tcpus = in_handle->cpus;\n\n\tdata = calloc(cpus, sizeof(struct cpu_data_source));\n\tif (!data)\n\t\treturn -1;\n\n\tfor (i = 0; i < in_handle->cpus; i++) {\n\t\tj = in_handle->cpu_data[i].cpu;\n\t\tdata[j].size = in_handle->cpu_data[i].file_size;\n\t\ttotal_size += data[j].size;\n\t\tif (in_handle->cpu_data[i].compress.fd >= 0) {\n\t\t\tdata[j].fd = in_handle->cpu_data[i].compress.fd;\n\t\t\tdata[j].offset = 0;\n\t\t} else {\n\t\t\tdata[j].fd = in_handle->fd;\n\t\t\tdata[j].offset = in_handle->cpu_data[i].file_offset;\n\t\t}\n\t}\n\tif (total_size || tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS)\n\t\tret = tcmd_out_write_cpu_data(out_handle, cpus, data, buff_name);\n\telse\n\t\tret = 0;\n\tfree(data);\n\n\treturn ret;\n}\n\nstatic int copy_flyrecord_buffer(struct tracecmd_input *in_handle,\n\t\t\t\t struct tracecmd_output *out_handle, int index)\n{\n\tstruct tracecmd_input *instance;\n\tconst char *name;\n\tint ret;\n\n\tname = tracecmd_buffer_instance_name(in_handle, index);\n\tif (!name)\n\t\treturn -1;\n\n\tinstance = tracecmd_buffer_instance_handle(in_handle, index);\n\tif (!instance)\n\t\treturn -1;\n\n\tif (!tracecmd_get_quiet(out_handle) && *name)\n\t\tfprintf(stderr, \"\\nBuffer: %s\\n\\n\", name);\n\n\tif (in_handle->buffers[index].latency)\n\t\tret = copy_trace_latency(in_handle, out_handle, name);\n\telse\n\t\tret = copy_trace_flyrecord_data(instance, out_handle, name);\n\ttracecmd_close(instance);\n\n\treturn ret;\n}\n\nstatic int copy_trace_data_from_v6(struct tracecmd_input *in_handle,\n\t\t\t\t   struct tracecmd_output *out_handle)\n{\n\tchar buf[10];\n\tint ret;\n\tint i;\n\n\tif (do_read_check(in_handle, buf, 10))\n\t\treturn -1;\n\n\tif (strncmp(buf, \"latency\", 7) == 0)\n\t\tin_handle->file_state = TRACECMD_FILE_CPU_LATENCY;\n\telse if (strncmp(buf, \"flyrecord\", 9) == 0)\n\t\tin_handle->file_state = TRACECMD_FILE_CPU_FLYRECORD;\n\n\ttracecmd_init_data(in_handle);\n\ttracecmd_set_out_clock(out_handle, in_handle->trace_clock);\n\n\tif (in_handle->file_state == TRACECMD_FILE_CPU_LATENCY)\n\t\treturn copy_trace_latency(in_handle, out_handle, \"\");\n\n\t/* top instance */\n\tret = copy_trace_flyrecord_data(in_handle, out_handle, \"\");\n\tif (ret)\n\t\treturn ret;\n\n\tfor (i = 0; i < in_handle->nr_buffers; i++)\n\t\tcopy_flyrecord_buffer(in_handle, out_handle, i);\n\n\treturn 0;\n}\n\nstatic int copy_trace_data_from_v7(struct tracecmd_input *in_handle,\n\t\t\t\t   struct tracecmd_output *out_handle)\n{\n\tint ret;\n\tint i;\n\n\t/* Force using temporary files for trace data decompression */\n\tin_handle->read_zpage = false;\n\tret = tracecmd_init_data(in_handle);\n\tif (ret < 0)\n\t\treturn ret;\n\ttracecmd_set_out_clock(out_handle, in_handle->trace_clock);\n\n\t/* copy top buffer */\n\tif (in_handle->top_buffer.latency)\n\t\tret = copy_trace_latency(in_handle, out_handle, in_handle->top_buffer.name);\n\telse if (in_handle->top_buffer.cpus)\n\t\tret = copy_trace_flyrecord_data(in_handle, out_handle,\n\t\t\t\t\t\tin_handle->top_buffer.name);\n\telse if (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS)\n\t\tret = tcmd_out_write_emty_cpu_data(out_handle, in_handle->max_cpu);\n\tif (ret)\n\t\treturn ret;\n\n\tfor (i = 0; i < in_handle->nr_buffers; i++)\n\t\tcopy_flyrecord_buffer(in_handle, out_handle, i);\n\n\treturn 0;\n}\n\n__hidden int tcmd_copy_trace_data(struct tracecmd_input *in_handle,\n\t\t\t\t      struct tracecmd_output *out_handle)\n{\n\tint ret;\n\n\tif (!check_in_state(in_handle, TRACECMD_FILE_CPU_FLYRECORD) ||\n\t    !tcmd_check_out_state(out_handle, TRACECMD_FILE_CPU_FLYRECORD))\n\t\treturn -1;\n\n\tif (in_handle->file_version < FILE_VERSION_SECTIONS)\n\t\tret = copy_trace_data_from_v6(in_handle, out_handle);\n\telse\n\t\tret = copy_trace_data_from_v7(in_handle, out_handle);\n\n\treturn ret;\n}\n\n/**\n * tracecmd_record_at_buffer_start - return true if record is first on subbuffer\n * @handle: input handle for the trace.dat file\n * @record: The record to test if it is the first record on page\n *\n * Returns true if the record is the first record on the page.\n */\nint tracecmd_record_at_buffer_start(struct tracecmd_input *handle,\n\t\t\t\t    struct tep_record *record)\n{\n\tstruct page *page = record->priv;\n\tstruct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf;\n\tint offset;\n\n\tif (!page || !kbuf)\n\t\treturn 0;\n\n\toffset = record->offset - page->offset;\n\treturn offset == kbuffer_start_of_data(kbuf);\n}\n\nunsigned long long tracecmd_page_ts(struct tracecmd_input *handle,\n\t\t\t\t    struct tep_record *record)\n{\n\tstruct page *page = record->priv;\n\tstruct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf;\n\n\tif (!page || !kbuf)\n\t\treturn 0;\n\n\treturn kbuffer_subbuf_timestamp(kbuf, page->map);\n}\n\nunsigned int tracecmd_record_ts_delta(struct tracecmd_input *handle,\n\t\t\t\t      struct tep_record *record)\n{\n\tstruct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf;\n\tstruct page *page = record->priv;\n\tint offset;\n\n\tif (!page || !kbuf)\n\t\treturn 0;\n\n\toffset = record->offset - page->offset;\n\n\treturn kbuffer_ptr_delta(kbuf, page->map + offset);\n}\n\nstruct kbuffer *tracecmd_record_kbuf(struct tracecmd_input *handle,\n\t\t\t\t     struct tep_record *record)\n{\n\treturn handle->cpu_data[record->cpu].kbuf;\n}\n\nvoid *tracecmd_record_page(struct tracecmd_input *handle,\n\t\t\t   struct tep_record *record)\n{\n\tstruct page *page = record->priv;\n\n\treturn page ? page->map : NULL;\n}\n\nvoid *tracecmd_record_offset(struct tracecmd_input *handle,\n\t\t\t     struct tep_record *record)\n{\n\tstruct page *page = record->priv;\n\tint offset;\n\n\tif (!page)\n\t\treturn NULL;\n\n\toffset = record->offset - page->offset;\n\n\treturn page->map + offset;\n}\n\nint tracecmd_buffer_instances(struct tracecmd_input *handle)\n{\n\treturn handle->nr_buffers;\n}\n\nconst char *tracecmd_buffer_instance_name(struct tracecmd_input *handle, int indx)\n{\n\tif (indx >= handle->nr_buffers)\n\t\treturn NULL;\n\n\treturn handle->buffers[indx].name;\n}\n\nstruct tracecmd_input *\ntracecmd_buffer_instance_handle(struct tracecmd_input *handle, int indx)\n{\n\tstruct tracecmd_input *new_handle;\n\tstruct input_buffer_instance *buffer = &handle->buffers[indx];\n\tsize_t offset;\n\tssize_t ret;\n\n\tif (indx >= handle->nr_buffers)\n\t\treturn NULL;\n\n\t/*\n\t * We make a copy of the current handle, but we substitute\n\t * the cpu data with the cpu data for this buffer.\n\t */\n\tnew_handle = malloc(sizeof(*handle));\n\tif (!new_handle)\n\t\treturn NULL;\n\n\t*new_handle = *handle;\n\tmemset(&new_handle->top_buffer, 0, sizeof(new_handle->top_buffer));\n\tnew_handle->cpu_data = NULL;\n\tnew_handle->nr_buffers = 0;\n\tnew_handle->buffers = NULL;\n\tnew_handle->version = NULL;\n\tnew_handle->sections = NULL;\n\tnew_handle->strings = NULL;\n\tnew_handle->guest = NULL;\n\tnew_handle->ref = 1;\n\tif (handle->trace_clock) {\n\t\tnew_handle->trace_clock = strdup(handle->trace_clock);\n\t\tif (!new_handle->trace_clock) {\n\t\t\tfree(new_handle);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tmemset(&new_handle->host, 0, sizeof(new_handle->host));\n\tnew_handle->parent = handle;\n\tnew_handle->cpustats = NULL;\n\tnew_handle->hooks = NULL;\n\tif (handle->uname)\n\t\t/* Ignore if fails to malloc, no biggy */\n\t\tnew_handle->uname = strdup(handle->uname);\n\ttracecmd_ref(handle);\n\n\tnew_handle->fd = dup(handle->fd);\n\n\tnew_handle->flags |= TRACECMD_FL_BUFFER_INSTANCE;\n\n\tnew_handle->pid_maps = NULL;\n\tif (!HAS_SECTIONS(handle)) {\n\t\t/* Save where we currently are */\n\t\toffset = lseek(handle->fd, 0, SEEK_CUR);\n\n\t\tret = lseek(handle->fd, buffer->offset, SEEK_SET);\n\t\tif (ret == (off_t)-1) {\n\t\t\ttracecmd_warning(\"could not seek to buffer %s offset %ld\",\n\t\t\t\t\t  buffer->name, buffer->offset);\n\t\t\tgoto error;\n\t\t}\n\t\t/*\n\t\t * read_options_type() is called right after the CPU count so update\n\t\t * file state accordingly.\n\t\t */\n\t\tnew_handle->file_state = TRACECMD_FILE_CPU_COUNT;\n\t\tret = read_options_type(new_handle);\n\t\tif (!ret)\n\t\t\tret = read_cpu_data(new_handle);\n\n\t\tif (ret < 0) {\n\t\t\ttracecmd_warning(\"failed to read sub buffer %s\", buffer->name);\n\t\t\tgoto error;\n\t\t}\n\t\tret = lseek(handle->fd, offset, SEEK_SET);\n\t\tif (ret < 0) {\n\t\t\ttracecmd_warning(\"could not seek to back to offset %ld\", offset);\n\t\t\tgoto error;\n\t\t}\n\t} else {\n\t\tnew_handle->page_size = handle->buffers[indx].page_size;\n\t\tif (init_buffer_cpu_data(new_handle, buffer) < 0)\n\t\t\tgoto error;\n\t}\n\n\treturn new_handle;\n\nerror:\n\ttracecmd_close(new_handle);\n\treturn NULL;\n}\n\nint tracecmd_is_buffer_instance(struct tracecmd_input *handle)\n{\n\treturn handle->flags & TRACECMD_FL_BUFFER_INSTANCE;\n}\n\n/**\n * tracecmd_long_size - return the size of \"long\" for the arch\n * @handle: input handle for the trace.dat file\n */\nint tracecmd_long_size(struct tracecmd_input *handle)\n{\n\treturn handle->long_size;\n}\n\n/**\n * tracecmd_page_size - return the PAGE_SIZE for the arch\n * @handle: input handle for the trace.dat file\n */\nint tracecmd_page_size(struct tracecmd_input *handle)\n{\n\treturn handle->page_size;\n}\n\n/**\n * tracecmd_cpus - return the number of CPUs recorded\n * @handle: input handle for the trace.dat file\n */\nint tracecmd_cpus(struct tracecmd_input *handle)\n{\n\treturn handle->max_cpu;\n}\n\n/**\n * tracecmd_get_tep - return the tep handle\n * @handle: input handle for the trace.dat file\n */\nstruct tep_handle *tracecmd_get_tep(struct tracecmd_input *handle)\n{\n\treturn handle->pevent;\n}\n\n/**\n * tracecmd_get_in_file_version - return the trace.dat file version\n * @handle: input handle for the trace.dat file\n */\nunsigned long tracecmd_get_in_file_version(struct tracecmd_input *handle)\n{\n\treturn handle->file_version;\n}\n\n/**\n * tracecmd_get_file_compress_proto - get name and version of compression algorithm\n * @handle: input handle for the trace.dat file\n * @name: return, name of the compression algorithm.\n * @version: return, version of the compression algorithm.\n *\n * Get the name and the version of the compression algorithm, used to\n * compress the file associated with @handle.\n * Returns 0 on success, or -1 in case of an error. If 0 is returned,\n * the name and version of the algorithm are stored in @name and @version.\n * The returned strings must *not* be freed.\n */\nint tracecmd_get_file_compress_proto(struct tracecmd_input *handle,\n\t\t\t\t     const char **name, const char **version)\n{\n\treturn tracecmd_compress_proto_get_name(handle->compress, name, version);\n}\n\n/**\n * tracecmd_get_use_trace_clock - return use_trace_clock\n * @handle: input handle for the trace.dat file\n */\nbool tracecmd_get_use_trace_clock(struct tracecmd_input *handle)\n{\n\treturn handle->use_trace_clock;\n}\n\n/**\n * tracecmd_get_options_offset - get offset of the options sections in the file\n * @handle: input handle for the trace.dat file\n */\nsize_t tracecmd_get_options_offset(struct tracecmd_input *handle)\n{\n\treturn handle->options_start;\n}\n\n/**\n * tracecmd_get_trace_clock - return the saved trace clock\n * @handle: input handle for the trace.dat file\n *\n * Returns a string of the clock that was saved in the trace.dat file.\n * The string should not be freed, as it points to the internal\n * structure data.\n */\nconst char *tracecmd_get_trace_clock(struct tracecmd_input *handle)\n{\n\treturn handle->trace_clock;\n}\n\n/**\n * tracecmd_get_tsc2nsec - get the calculation numbers to convert to nsecs\n * @mult: If not NULL, points to where to save the multiplier\n * @shift: If not NULL, points to where to save the shift.\n * @offset: If not NULL, points to where to save the offset.\n *\n * This only returns a value if the clock is of a raw type.\n * (currently just x86-tsc is supported).\n *\n * Returns 0 on success, or -1 on not supported clock (but may still fill\n * in the values).\n */\nint tracecmd_get_tsc2nsec(struct tracecmd_input *handle,\n\t\t\t  int *mult, int *shift, unsigned long long *offset)\n{\n\tif (mult)\n\t\t*mult = handle->tsc_calc.mult;\n\tif (shift)\n\t\t*shift = handle->tsc_calc.shift;\n\tif (offset)\n\t\t*offset = handle->tsc_calc.offset;\n\n\treturn handle->top_buffer.clock &&\n\t\t(strcmp(handle->top_buffer.clock, \"x86-tsc\") == 0 ||\n\t\t strcmp(handle->top_buffer.clock, \"tsc2nsec\") == 0) ? 0 : -1;\n}\n\n/**\n * tracecmd_get_cpustats - return the saved cpu stats\n * @handle: input handle for the trace.dat file\n *\n * Provides a method to extract the cpu stats saved in @handle.\n *\n * Returns a string of the cpu stats that was saved in the trace.dat file.\n * The string should not be freed, as it points to the internal\n * structure data.\n */\nconst char *tracecmd_get_cpustats(struct tracecmd_input *handle)\n{\n\treturn handle->cpustats;\n}\n\n/**\n * tracecmd_get_uname - return the saved name and kernel information\n * @handle: input handle for the trace.dat file\n *\n * Provides a method to extract the system information saved in @handle.\n *\n * Returns a string of the system information that was saved in the\n * trace.dat file.\n * The string should not be freed, as it points to the internal\n * structure data.\n */\nconst char *tracecmd_get_uname(struct tracecmd_input *handle)\n{\n\treturn handle->uname;\n}\n\n/**\n * tracecmd_get_version - return the saved version information\n * @handle: input handle for the trace.dat file\n *\n * Provides a method to extract the version string saved in @handle.\n *\n * Returns a string of the version that was saved in the trace.dat file.\n * The string should not be freed, as it points to the internal\n * structure data.\n */\nconst char *tracecmd_get_version(struct tracecmd_input *handle)\n{\n\treturn handle->version;\n}\n\n/**\n * tracecmd_get_cpu_file_size - return the saved cpu file size\n * @handle: input handle for the trace.dat file\n * @cpu: cpu index\n *\n * Provides a method to extract the cpu file size saved in @handle.\n *\n * Returns the cpu file size saved in trace.dat file or (off_t)-1 for\n * invalid cpu index.\n */\noff_t tracecmd_get_cpu_file_size(struct tracecmd_input *handle, int cpu)\n{\n\tif (cpu < 0 || cpu >= handle->cpus)\n\t\treturn (off_t)-1;\n\treturn handle->cpu_data[cpu].file_size;\n}\n\n/**\n * tracecmd_get_show_data_func - return the show data func\n * @handle: input handle for the trace.dat file\n */\ntracecmd_show_data_func\ntracecmd_get_show_data_func(struct tracecmd_input *handle)\n{\n\treturn handle->show_data_func;\n}\n\n/**\n * tracecmd_set_show_data_func - set the show data func\n * @handle: input handle for the trace.dat file\n */\nvoid tracecmd_set_show_data_func(struct tracecmd_input *handle,\n\t\t\t\t tracecmd_show_data_func func)\n{\n\thandle->show_data_func = func;\n}\n\n/**\n * tracecmd_get_traceid - get the trace id of the session\n * @handle: input handle for the trace.dat file\n *\n * Returns the trace id, written in the trace file\n */\nunsigned long long tracecmd_get_traceid(struct tracecmd_input *handle)\n{\n\treturn handle->trace_id;\n}\n\n/**\n * tracecmd_get_first_ts - get the timestamp of the first recorded event\n * @handle: input handle for the trace.dat file\n *\n * Returns the timestamp of the first recorded event\n */\nunsigned long long tracecmd_get_first_ts(struct tracecmd_input *handle)\n{\n\tunsigned long long ts = 0;\n\tbool first = true;\n\tint i;\n\n\tfor (i = 0; i < handle->cpus; i++) {\n\t\t/* Ignore empty buffers */\n\t\tif (!handle->cpu_data[i].size)\n\t\t\tcontinue;\n\t\tif (first || ts > handle->cpu_data[i].first_ts)\n\t\t\tts = handle->cpu_data[i].first_ts;\n\t\tfirst = false;\n\t}\n\n\treturn ts;\n}\n\n/**\n * tracecmd_get_guest_cpumap - get the mapping of guest VCPU to host process\n * @handle: input handle for the trace.dat file\n * @trace_id: ID of the guest tracing session\n * @name: return, name of the guest\n * @vcpu_count: return, number of VPUs\n * @cpu_pid: return, array with guest VCPU to host process mapping\n *\n * Returns @name of the guest, number of VPUs (@vcpu_count)\n * and array @cpu_pid with size @vcpu_count. Array index is VCPU id, array\n * content is PID of the host process, running this VCPU.\n *\n * This information is stored in host trace.dat file\n */\nint tracecmd_get_guest_cpumap(struct tracecmd_input *handle,\n\t\t\t      unsigned long long trace_id,\n\t\t\t      const char **name,\n\t\t\t      int *vcpu_count, const int **cpu_pid)\n{\n\tstruct guest_trace_info\t*guest = handle->guest;\n\n\twhile (guest) {\n\t\tif (guest->trace_id == trace_id)\n\t\t\tbreak;\n\t\tguest = guest->next;\n\t}\n\tif (!guest)\n\t\treturn -1;\n\n\tif (name)\n\t\t*name = guest->name;\n\tif (vcpu_count)\n\t\t*vcpu_count = guest->vcpu_count;\n\tif (cpu_pid)\n\t\t*cpu_pid = guest->cpu_pid;\n\treturn 0;\n}\n\n/**\n * tracecmd_enable_tsync - enable / disable the timestamps correction\n * @handle: input handle for the trace.dat file\n * @enable: enable / disable the timestamps correction\n *\n * Enables or disables timestamps correction on file load, using the array of\n * recorded time offsets. If \"enable\" is true, but there are no time offsets,\n * function fails and -1 is returned.\n *\n * Returns -1 in case of an error, or 0 otherwise\n */\nint tracecmd_enable_tsync(struct tracecmd_input *handle, bool enable)\n{\n\tif (enable &&\n\t    (!handle->host.ts_offsets || !handle->host.cpu_count))\n\t\treturn -1;\n\n\thandle->host.sync_enable = enable;\n\n\treturn 0;\n}\n\n__hidden struct tracecmd_filter *tcmd_filter_get(struct tracecmd_input *handle)\n{\n\treturn handle->filter;\n}\n\n__hidden void tcmd_filter_set(struct tracecmd_input *handle,\n\t\t\t      struct tracecmd_filter *filter)\n{\n\t/* This can be used to set filter to NULL though. */\n\tif (handle->filter && filter) {\n\t\ttracecmd_warning(\"Filter exists and setting a new one\");\n\t\treturn;\n\t}\n\n\thandle->filter = filter;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-maps.c",
    "content": "#include <stdlib.h>\n\n#include \"trace-cmd-local.h\"\n#include \"trace-local.h\"\n\n/*\n * Structure to hold the mapping between host and guest.\n * @self - A pointer back to the guest's mapping (for the host copy to use)\n * @host_handle - The handle for the host for this mapping.\n * @guest_handle - The handle for the guest for this mapping.\n * @guest_vcpu - The vCPU # for this mapping.\n * @host_pid - The pid of the task on the host that runs when this vCPU executes.\n * @private - Private data for applications to use.\n */\nstruct tracecmd_cpu_map {\n\tstruct tracecmd_cpu_map\t\t*self;\n\tstruct tracecmd_input\t\t*host_handle;\n\tstruct tracecmd_input\t\t*guest_handle;\n\tint\t\t\t\tguest_vcpu;\n\tint\t\t\t\thost_pid;\n\tvoid\t\t\t\t*private;\n};\n\nstatic int cmp_map(const void *A, const void *B)\n{\n\tconst struct tracecmd_cpu_map *a = A;\n\tconst struct tracecmd_cpu_map *b = B;\n\n\tif (a->host_pid < b->host_pid)\n\t\treturn -1;\n\treturn a->host_pid > b->host_pid;\n}\n\nint tracecmd_map_vcpus(struct tracecmd_input **handles, int nr_handles)\n{\n\tstruct tracecmd_input *host_handle = handles[0];\n\tunsigned long long traceid;\n\tstruct tracecmd_cpu_map *vcpu_maps = NULL;\n\tstruct tracecmd_cpu_map *gmap;\n\tstruct tracecmd_cpu_map *map;\n\tconst int *cpu_pids;\n\tconst char *name;\n\tint nr_vcpu_maps = 0;\n\tint vcpu_count;\n\tint mappings = 0;\n\tint ret;\n\tint i, k;\n\n\t/* handles[0] is the host handle, do for each guest handle */\n\tfor (i = 1; i < nr_handles; i++) {\n\t\ttraceid = tracecmd_get_traceid(handles[i]);\n\n\t\t/*\n\t\t * Retrieve the host mapping of the guest for this handle.\n\t\t * cpu_pids is an array of pids that map 1-1 the host vcpus where\n\t\t * cpu_pids[vCPU_num] = host_task_pid\n\t\t */\n\t\tret = tracecmd_get_guest_cpumap(host_handle, traceid,\n\t\t\t\t\t\t&name, &vcpu_count, &cpu_pids);\n\t\tif (ret)\n\t\t\tcontinue;\n\n\t\tmappings++;\n\n\t\tgmap = calloc(sizeof(*gmap), vcpu_count);\n\t\tif (!gmap)\n\t\t\tgoto fail;\n\n\t\tfor (k = 0; k < vcpu_count; k++) {\n\t\t\tgmap[k].host_handle = handles[0];\n\t\t\tgmap[k].guest_handle = handles[i];\n\t\t\tgmap[k].guest_vcpu = k;\n\t\t\tgmap[k].host_pid = cpu_pids[k];\n\t\t\tgmap[k].self = &gmap[k];\n\t\t}\n\n\t\ttcmd_set_guest_map(handles[i], gmap);\n\t\ttcmd_set_guest_map_cnt(handles[i], vcpu_count);\n\n\t\t/* Update the host mapping of all guests to the host */\n\t\tmap = realloc(vcpu_maps, sizeof(*map) * (nr_vcpu_maps + vcpu_count));\n\t\tif (!map)\n\t\t\tgoto fail;\n\t\tmemset(map + nr_vcpu_maps, 0, sizeof(*map) * (vcpu_count - nr_vcpu_maps));\n\n\t\tvcpu_maps = map;\n\t\tmap += nr_vcpu_maps;\n\t\tnr_vcpu_maps += vcpu_count;\n\n\t\tfor (k = 0; k < vcpu_count; k++)\n\t\t\tmap[k] = gmap[k];\n\t}\n\tif (!vcpu_maps)\n\t\treturn 0;\n\n\t/* We want to do a binary search via host_pid to find these mappings */\n\tqsort(vcpu_maps, nr_vcpu_maps, sizeof(*map), cmp_map);\n\n\ttcmd_set_guest_map(handles[0], vcpu_maps);\n\ttcmd_set_guest_map_cnt(handles[0], nr_vcpu_maps);\n\n\treturn mappings;\n\n fail:\n\tfree(vcpu_maps);\n\treturn -1;\n}\n\n__hidden void tcmd_guest_map_free(struct tracecmd_cpu_map *map)\n{\n\tfree(map);\n}\n\nstruct tracecmd_cpu_map *tracecmd_map_find_by_host_pid(struct tracecmd_input *handle,\n\t\t\t\t\t\t       int host_pid)\n{\n\tstruct tracecmd_cpu_map *map;\n\tstruct tracecmd_cpu_map key;\n\tint nr_maps;\n\n\tmap = tcmd_get_guest_map(handle);\n\tif (!map)\n\t\treturn NULL;\n\n\t/* The handle could be from the guest, get the host handle */\n\thandle = map->host_handle;\n\n\t/* And again, get the mapping of the host, as it has all the mappings */\n\tmap = tcmd_get_guest_map(handle);\n\tif (!map)\n\t\treturn NULL;\n\n\tnr_maps = tcmd_get_guest_map_cnt(handle);\n\n\tkey.host_pid = host_pid;\n\n\tmap = bsearch(&key, map, nr_maps, sizeof(*map), cmp_map);\n\n\treturn map ? map->self : NULL;\n}\n\nvoid tracecmd_map_set_private(struct tracecmd_cpu_map *map, void *priv)\n{\n\t/* Only set the guest private */\n\tmap = map->self;\n\tmap->private = priv;\n}\n\nvoid *tracecmd_map_get_private(struct tracecmd_cpu_map *map)\n{\n\t/* Return the guest private */\n\tmap = map->self;\n\treturn map->private;\n}\n\nstruct tracecmd_input *tracecmd_map_get_guest(struct tracecmd_cpu_map *map)\n{\n\treturn map->guest_handle;\n}\n\nint tracecmd_map_get_host_pid(struct tracecmd_cpu_map *map)\n{\n\treturn map->host_pid;\n}\n\nstruct tracecmd_cpu_map *tracecmd_get_cpu_map(struct tracecmd_input *handle, int cpu)\n{\n\tstruct tracecmd_cpu_map *map;\n\tint cnt;\n\n\tmap = tcmd_get_guest_map(handle);\n\t/* Make sure it's for the guest handle, as this could be a host handle */\n\tmap = map->self;\n\tcnt = tcmd_get_guest_map_cnt(map->guest_handle);\n\tif (cnt <= cpu)\n\t\treturn NULL;\n\n\treturn map + cpu;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-msg.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * trace-msg.c : define message protocol for communication between clients and\n *               a server\n *\n * Copyright (C) 2013 Hitachi, Ltd.\n * Created by Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>\n *\n */\n\n#include <errno.h>\n#include <poll.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n#include <unistd.h>\n#include <arpa/inet.h>\n#include <sys/mman.h>\n#include <sys/types.h>\n#include <linux/types.h>\n\n#include \"trace-write-local.h\"\n#include \"trace-cmd-local.h\"\n#include \"trace-local.h\"\n#include \"trace-msg.h\"\n#include \"trace-cmd.h\"\n\ntypedef __u32 u32;\ntypedef __be32 be32;\n\n#define dprint(fmt, ...)\ttracecmd_debug(fmt, ##__VA_ARGS__)\n\n/* Two (4k) pages is the max transfer for now */\n#define MSG_MAX_LEN\t\t\t8192\n\n#define MSG_HDR_LEN\t\t\tsizeof(struct tracecmd_msg_header)\n\n#define MSG_MAX_DATA_LEN\t\t(MSG_MAX_LEN - MSG_HDR_LEN)\n\nunsigned int page_size;\n\nstruct tracecmd_msg_tinit {\n\tbe32 cpus;\n\tbe32 page_size;\n\tbe32 opt_num;\n} __packed;\n\nstruct tracecmd_msg_rinit {\n\tbe32 cpus;\n} __packed;\n\n#define TRACE_REQ_PARAM_SIZE  (2 * sizeof(int))\nenum trace_req_params {\n\tTRACE_REQUEST_ARGS,\n\tTRACE_REQUEST_TSYNC_PROTOS,\n};\n\nstruct tracecmd_msg_trace_req_param {\n\tint id;\n\tint length;\n\tchar *value;\n};\n\nstruct tracecmd_msg_trace_req {\n\tbe32 flags;\n\tbe32 argc;\n\tu64 trace_id;\n} __packed;\n\nstruct tracecmd_msg_trace_proxy {\n\tstruct tracecmd_msg_trace_req req;\n\tbe32 cpus;\n\tbe32 siblings;\n} __packed;\n\nstruct tracecmd_msg_trace_resp {\n\tbe32 flags;\n\tbe32 cpus;\n\tbe32 page_size;\n\tu64 trace_id;\n\tchar tsync_proto_name[TRACECMD_TSYNC_PNAME_LENGTH];\n\tbe32 tsync_port;\n} __packed;\n\nstruct tracecmd_msg_tsync {\n\tchar sync_protocol_name[TRACECMD_TSYNC_PNAME_LENGTH];\n\tbe32 sync_msg_id;\n} __packed;\n\nstruct tracecmd_msg_header {\n\tbe32\tsize;\n\tbe32\tcmd;\n\tbe32\tcmd_size;\n} __packed;\n\n#define MSG_MAP\t\t\t\t\t\t\t\t\\\n\tC(CLOSE,\t0,\t0),\t\t\t\t\t\\\n\tC(TINIT,\t1,\tsizeof(struct tracecmd_msg_tinit)),\t\\\n\tC(RINIT,\t2,\tsizeof(struct tracecmd_msg_rinit)),\t\\\n\tC(SEND_DATA,\t3,\t0),\t\t\t\t\t\\\n\tC(FIN_DATA,\t4,\t0),\t\t\t\t\t\\\n\tC(NOT_SUPP,\t5,\t0),\t\t\t\t\t\\\n\tC(TRACE_REQ,\t6,\tsizeof(struct tracecmd_msg_trace_req)),\t\\\n\tC(TRACE_RESP,\t7,\tsizeof(struct tracecmd_msg_trace_resp)),\\\n\tC(CLOSE_RESP,\t8,\t0),\t\t\t\t\t\\\n\tC(TIME_SYNC,\t9,\tsizeof(struct tracecmd_msg_tsync)),\t\\\n\tC(TRACE_PROXY,\t10,\tsizeof(struct tracecmd_msg_trace_proxy)), \\\n\tC(CONT,\t\t11,\t0),\n\n#undef C\n#define C(a,b,c)\tMSG_##a = b\n\nenum tracecmd_msg_cmd {\n\tMSG_MAP\n\tMSG_NR_COMMANDS\n};\n\n#undef C\n#define C(a,b,c)\tc\n\nstatic be32 msg_cmd_sizes[] = { MSG_MAP };\n\n#undef C\n#define C(a,b,c)\t#a\n\nstatic const char *msg_names[] = { MSG_MAP };\n\nstatic const char *cmd_to_name(int cmd)\n{\n\tif (cmd < 0 || cmd >= MSG_NR_COMMANDS)\n\t\treturn \"Unknown\";\n\treturn msg_names[cmd];\n}\n\nstruct tracecmd_msg {\n\tstruct tracecmd_msg_header\t\thdr;\n\tunion {\n\t\tstruct tracecmd_msg_tinit\ttinit;\n\t\tstruct tracecmd_msg_rinit\trinit;\n\t\tstruct tracecmd_msg_trace_req\ttrace_req;\n\t\tstruct tracecmd_msg_trace_proxy\ttrace_proxy;\n\t\tstruct tracecmd_msg_trace_resp\ttrace_resp;\n\t\tstruct tracecmd_msg_tsync\ttsync;\n\t};\n\tchar\t\t\t\t\t*buf;\n} __packed;\n\nstatic inline int msg_buf_len(struct tracecmd_msg *msg)\n{\n\treturn ntohl(msg->hdr.size) - MSG_HDR_LEN - ntohl(msg->hdr.cmd_size);\n}\n\nstatic int __msg_write(int fd, struct tracecmd_msg *msg, bool network)\n{\n\tint msg_size, data_size;\n\tint ret;\n\tint cmd;\n\n\tif (network) {\n\t\tcmd = ntohl(msg->hdr.cmd);\n\t\tif (cmd < 0 || cmd >= MSG_NR_COMMANDS)\n\t\t\treturn -EINVAL;\n\t\tdprint(\"msg send: %d (%s) [%d]\\n\",\n\t\t       cmd, cmd_to_name(cmd), ntohl(msg->hdr.size));\n\t}\n\tmsg_size = MSG_HDR_LEN + ntohl(msg->hdr.cmd_size);\n\tdata_size = ntohl(msg->hdr.size) - msg_size;\n\tif (data_size < 0)\n\t\treturn -EINVAL;\n\n\tif (network) {\n\t\tret = __do_write_check(fd, msg, msg_size);\n\t\tif (ret < 0)\n\t\t\treturn ret;\n\t}\n\tif (!data_size)\n\t\treturn 0;\n\n\treturn __do_write_check(fd, msg->buf, data_size);\n}\n\n__hidden off_t tcmd_msg_lseek(struct tracecmd_msg_handle *msg_handle, off_t offset, int whence)\n{\n\toff_t cache_offset = msg_handle->cache_start_offset;\n\toff_t ret;\n\n\t/*\n\t * lseek works only if the handle is in cache mode,\n\t * cannot seek on a network socket\n\t */\n\tif (!msg_handle->cache || msg_handle->cfd < 0)\n\t\treturn (off_t)-1;\n\n\tif (whence == SEEK_SET) {\n\t\tif (offset < cache_offset)\n\t\t\treturn (off_t)-1;\n\t\toffset -= cache_offset;\n\t}\n\n\tret = lseek(msg_handle->cfd, offset, whence);\n\tif (ret == (off_t)-1)\n\t\treturn ret;\n\n\treturn ret + cache_offset;\n}\n\nstatic int msg_write(struct tracecmd_msg_handle *msg_handle, struct tracecmd_msg *msg)\n{\n\tif (msg_handle->cache && msg_handle->cfd >= 0)\n\t\treturn __msg_write(msg_handle->cfd, msg, false);\n\n\n\treturn __msg_write(msg_handle->fd, msg, true);\n}\n\nenum msg_trace_flags {\n\tMSG_TRACE_USE_FIFOS = 1 << 0,\n};\n\nstatic int make_tinit(struct tracecmd_msg_handle *msg_handle,\n\t\t      struct tracecmd_msg *msg)\n{\n\tint cpu_count = msg_handle->cpu_count;\n\tint opt_num = 0;\n\tint data_size = 0;\n\n\tif (msg_handle->flags & (TRACECMD_MSG_FL_USE_TCP |\n\t\t\t\t TRACECMD_MSG_FL_USE_VSOCK)) {\n\t\tmsg->buf = msg_handle->flags & TRACECMD_MSG_FL_USE_TCP ?\n\t\t\tstrdup(\"tcp\") : strdup(\"vsock\");\n\t\tif (!msg->buf)\n\t\t\treturn -1;\n\t\topt_num++;\n\t\tdata_size += strlen(msg->buf) + 1;\n\t}\n\n\tmsg->tinit.cpus = htonl(cpu_count);\n\tmsg->tinit.page_size = htonl(page_size);\n\tmsg->tinit.opt_num = htonl(opt_num);\n\n\tmsg->hdr.size = htonl(ntohl(msg->hdr.size) + data_size);\n\n\treturn 0;\n}\n\n/* test a to u */\nstatic int tatou(const char *s, unsigned int *res)\n{\n        long r;\n\n        r = atol(s);\n        if (r >= 0 && r <= UINT_MAX) {\n                *res = (unsigned int)r;\n                return 0;\n        }\n        return -1;\n}\n\nstatic int write_uints(char *buf, size_t buf_len,\n\t\t       unsigned int *arr, int arr_len)\n{\n\tint i, ret, tot = 0;\n\n\tfor (i = 0; i < arr_len; i++) {\n\t\tret = snprintf(buf, buf_len, \"%u\", arr[i]);\n\t\tif (ret < 0)\n\t\t\treturn ret;\n\n\t\t/* Count the '\\0' byte */\n\t\tret++;\n\t\ttot += ret;\n\t\tif (buf)\n\t\t\tbuf += ret;\n\t\tif (buf_len >= ret)\n\t\t\tbuf_len -= ret;\n\t\telse\n\t\t\tbuf_len = 0;\n\t}\n\n\treturn tot;\n}\n\nstatic int make_rinit(struct tracecmd_msg *msg, int cpus, unsigned int *ports)\n{\n\tint data_size;\n\n\tdata_size = write_uints(NULL, 0, ports, cpus);\n\tmsg->buf = malloc(data_size);\n\tif (!msg->buf)\n\t\treturn -ENOMEM;\n\twrite_uints(msg->buf, data_size, ports, cpus);\n\n\tmsg->rinit.cpus = htonl(cpus);\n\tmsg->hdr.size = htonl(ntohl(msg->hdr.size) + data_size);\n\n\treturn 0;\n}\n\nstatic void tracecmd_msg_init(u32 cmd, struct tracecmd_msg *msg)\n{\n\tmemset(msg, 0, sizeof(*msg));\n\tmsg->hdr.size = htonl(MSG_HDR_LEN + msg_cmd_sizes[cmd]);\n\tmsg->hdr.cmd = htonl(cmd);\n\tmsg->hdr.cmd_size = htonl(msg_cmd_sizes[cmd]);\n}\n\nstatic void msg_free(struct tracecmd_msg *msg)\n{\n\tfree(msg->buf);\n\tmemset(msg, 0, sizeof(*msg));\n}\n\nstatic int tracecmd_msg_send(struct tracecmd_msg_handle *msg_handle, struct tracecmd_msg *msg)\n{\n\tint ret = 0;\n\n\tret = msg_write(msg_handle, msg);\n\tif (ret < 0)\n\t\tret = -ECOMM;\n\n\tmsg_free(msg);\n\n\treturn ret;\n}\n\nstatic int msg_send_nofree(struct tracecmd_msg_handle *msg_handle, struct tracecmd_msg *msg)\n{\n\tint ret = 0;\n\n\tret = msg_write(msg_handle, msg);\n\tif (ret < 0)\n\t\tret = -ECOMM;\n\n\treturn ret;\n}\n\nstatic int msg_read(int fd, void *buf, u32 size, int *n)\n{\n\tssize_t r;\n\n\twhile (size) {\n\t\tr = read(fd, buf + *n, size);\n\t\tif (r < 0) {\n\t\t\tif (errno == EINTR)\n\t\t\t\tcontinue;\n\t\t\treturn -errno;\n\t\t} else if (!r)\n\t\t\treturn -ENOTCONN;\n\t\tsize -= r;\n\t\t*n += r;\n\t}\n\n\treturn 0;\n}\n\nstatic char scratch_buf[MSG_MAX_LEN];\n\nstatic int msg_read_extra(int fd, struct tracecmd_msg *msg,\n\t\t\t  int *n, int size)\n{\n\tint cmd, cmd_size, rsize;\n\tint ret;\n\n\tcmd = ntohl(msg->hdr.cmd);\n\tif (cmd < 0 || cmd >= MSG_NR_COMMANDS)\n\t\treturn -EINVAL;\n\n\tcmd_size = ntohl(msg->hdr.cmd_size);\n\tif (cmd_size < 0)\n\t\treturn -EINVAL;\n\n\tif (cmd_size > 0) {\n\t\trsize = cmd_size;\n\t\tif (rsize > msg_cmd_sizes[cmd])\n\t\t\trsize = msg_cmd_sizes[cmd];\n\n\t\tret = msg_read(fd, msg, rsize, n);\n\t\tif (ret < 0)\n\t\t\treturn ret;\n\n\t\tret = msg_read(fd, scratch_buf, cmd_size - rsize, n);\n\t\tif (ret < 0)\n\t\t\treturn ret;\n\t}\n\n\tif (size > *n) {\n\t\tsize -= *n;\n\t\tmsg->buf = malloc(size);\n\t\tif (!msg->buf)\n\t\t\treturn -ENOMEM;\n\n\t\t*n = 0;\n\t\treturn msg_read(fd, msg->buf, size, n);\n\t}\n\n\treturn 0;\n}\n\n/*\n * Read header information of msg first, then read all data\n */\nstatic int tracecmd_msg_recv(int fd, struct tracecmd_msg *msg)\n{\n\tu32 size = 0;\n\tint n = 0;\n\tint ret;\n\n\tret = msg_read(fd, msg, MSG_HDR_LEN, &n);\n\tif (ret < 0)\n\t\treturn ret;\n\n\tdprint(\"msg received: %d (%s) [%d]\\n\",\n\t       ntohl(msg->hdr.cmd), cmd_to_name(ntohl(msg->hdr.cmd)),\n\t       ntohl(msg->hdr.size));\n\n\tsize = ntohl(msg->hdr.size);\n\tif (size > MSG_MAX_LEN)\n\t\t/* too big */\n\t\tgoto error;\n\telse if (size < MSG_HDR_LEN)\n\t\t/* too small */\n\t\tgoto error;\n\telse if (size > MSG_HDR_LEN)\n\t\treturn msg_read_extra(fd, msg, &n, size);\n\n\treturn 0;\nerror:\n\ttracecmd_plog(\"Receive an invalid message(size=%d)\\n\", size);\n\treturn -ENOMSG;\n}\n\n#define MSG_WAIT_MSEC\t5000\nstatic int msg_wait_to = MSG_WAIT_MSEC;\n\nbool tracecmd_msg_done(struct tracecmd_msg_handle *msg_handle)\n{\n\treturn (volatile int)msg_handle->done;\n}\n\nvoid tracecmd_msg_set_done(struct tracecmd_msg_handle *msg_handle)\n{\n\tmsg_handle->done = true;\n}\n\nstatic void error_operation(struct tracecmd_msg *msg)\n{\n\ttracecmd_warning(\"Message: cmd=%d size=%d\", ntohl(msg->hdr.cmd), ntohl(msg->hdr.size));\n}\n\n/*\n * A return value of 0 indicates time-out\n */\nstatic int tracecmd_msg_recv_wait(int fd, struct tracecmd_msg *msg)\n{\n\tstruct pollfd pfd;\n\tint ret;\n\n\tpfd.fd = fd;\n\tpfd.events = POLLIN;\n\tret = poll(&pfd, 1, tracecmd_get_notimeout() ? -1 : msg_wait_to);\n\tif (ret < 0)\n\t\treturn -errno;\n\telse if (ret == 0)\n\t\treturn -ETIMEDOUT;\n\n\treturn tracecmd_msg_recv(fd, msg);\n}\n\nstatic int tracecmd_msg_wait_for_msg(int fd, struct tracecmd_msg *msg)\n{\n\tu32 cmd;\n\tint ret;\n\n\tret = tracecmd_msg_recv_wait(fd, msg);\n\tif (ret < 0) {\n\t\tif (ret == -ETIMEDOUT)\n\t\t\ttracecmd_warning(\"Connection timed out\");\n\t\treturn ret;\n\t}\n\n\tcmd = ntohl(msg->hdr.cmd);\n\tif (cmd == MSG_CLOSE)\n\t\treturn -ECONNABORTED;\n\n\treturn 0;\n}\n\nstatic int tracecmd_msg_send_notsupp(struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_msg msg;\n\n\ttracecmd_msg_init(MSG_NOT_SUPP, &msg);\n\treturn tracecmd_msg_send(msg_handle, &msg);\n}\n\nstatic int handle_unexpected_msg(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t struct tracecmd_msg *msg)\n{\n\t/* Don't send MSG_NOT_SUPP back if we just received one */\n\tif (ntohl(msg->hdr.cmd) == MSG_NOT_SUPP)\n\t\treturn 0;\n\n\treturn tracecmd_msg_send_notsupp(msg_handle);\n\n}\n\nint tracecmd_msg_send_init_data(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tunsigned int **client_ports)\n{\n\tstruct tracecmd_msg msg;\n\tunsigned int *ports;\n\tint i, cpus, ret;\n\tchar *p, *buf_end;\n\tssize_t buf_len;\n\n\t*client_ports = NULL;\n\n\ttracecmd_msg_init(MSG_TINIT, &msg);\n\tret = make_tinit(msg_handle, &msg);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tret = tracecmd_msg_send(msg_handle, &msg);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tmsg_free(&msg);\n\n\tret = tracecmd_msg_wait_for_msg(msg_handle->fd, &msg);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tif (ntohl(msg.hdr.cmd) != MSG_RINIT) {\n\t\tret = -EOPNOTSUPP;\n\t\tgoto error;\n\t}\n\n\tbuf_len = msg_buf_len(&msg);\n\tif (buf_len <= 0) {\n\t\tret = -EINVAL;\n\t\tgoto error;\n\t}\n\n\tif (msg.buf[buf_len-1] != '\\0') {\n\t\tret = -EINVAL;\n\t\tgoto error;\n\t}\n\n\tcpus = ntohl(msg.rinit.cpus);\n\tports = malloc(sizeof(*ports) * cpus);\n\tif (!ports) {\n\t\tret = -ENOMEM;\n\t\tgoto out;\n\t}\n\n\tbuf_end = msg.buf + buf_len;\n\tfor (i = 0, p = msg.buf; i < cpus; i++, p++) {\n\t\tif (p >= buf_end || tatou(p, &ports[i])) {\n\t\t\tfree(ports);\n\t\t\tret = -EINVAL;\n\t\t\tgoto error;\n\t\t}\n\t\tp = strchr(p, '\\0');\n\t}\n\n\t*client_ports = ports;\n\n\tmsg_free(&msg);\n\treturn 0;\n\nerror:\n\terror_operation(&msg);\n\tif (ret == -EOPNOTSUPP)\n\t\thandle_unexpected_msg(msg_handle, &msg);\nout:\n\tmsg_free(&msg);\n\treturn ret;\n}\n\nstatic bool process_option(struct tracecmd_msg_handle *msg_handle,\n\t\t\t   const char *opt)\n{\n\tif (strcmp(opt, \"tcp\") == 0) {\n\t\tmsg_handle->flags |= TRACECMD_MSG_FL_USE_TCP;\n\t\treturn true;\n\t}\n\tif (strcmp(opt, \"vsock\") == 0) {\n\t\tmsg_handle->flags |= TRACECMD_MSG_FL_USE_VSOCK;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstruct tracecmd_msg_handle *\ntracecmd_msg_handle_alloc(int fd, unsigned long flags)\n{\n\tstruct tracecmd_msg_handle *handle;\n\n\thandle = calloc(1, sizeof(struct tracecmd_msg_handle));\n\tif (!handle)\n\t\treturn NULL;\n\n\thandle->fd = fd;\n\thandle->flags = flags;\n\thandle->cfd = -1;\n\thandle->cache = false;\n\treturn handle;\n}\n\nint tracecmd_msg_handle_cache(struct tracecmd_msg_handle *msg_handle)\n{\n\tif (msg_handle->cfd < 0) {\n#ifdef HAVE_MEMFD_CREATE\n\t\tmsg_handle->cfd = memfd_create(\"trace_msg_cache\", 0);\n\t\tif (msg_handle->cfd < 0)\n\t\t\treturn -1;\n#else\n\t\tstrcpy(msg_handle->cfile, MSG_CACHE_FILE);\n\t\tmsg_handle->cfd = mkstemp(msg_handle->cfile);\n\t\tif (msg_handle->cfd < 0)\n\t\t\treturn -1;\n\t\tunlink(msg_handle->cfile);\n#endif\n\t}\n\tmsg_handle->cache = true;\n\treturn 0;\n}\n\nstatic int flush_cache(struct tracecmd_msg_handle *msg_handle)\n{\n\tchar buf[MSG_MAX_DATA_LEN];\n\tint fd = msg_handle->cfd;\n\tint ret;\n\n\tif (!msg_handle->cache || fd < 0)\n\t\treturn 0;\n\tmsg_handle->cache = false;\n\tif (lseek(fd, 0, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\tdo {\n\t\tret = read(fd, buf, MSG_MAX_DATA_LEN);\n\t\tif (ret <= 0)\n\t\t\tbreak;\n\t\tret = tracecmd_msg_data_send(msg_handle, buf, ret);\n\t\tif (ret < 0)\n\t\t\tbreak;\n\t} while (ret >= 0);\n\n\tmsg_handle->cache_start_offset = lseek(fd, 0, SEEK_CUR);\n\tif (msg_handle->cache_start_offset == (off_t)-1)\n\t\treturn -1;\n\n\tclose(fd);\n\tmsg_handle->cfd = -1;\n\treturn ret;\n}\n\nvoid tracecmd_msg_handle_close(struct tracecmd_msg_handle *msg_handle)\n{\n\tif (msg_handle->fd >= 0)\n\t\tclose(msg_handle->fd);\n\tif (msg_handle->cfd >= 0)\n\t\tclose(msg_handle->cfd);\n\tfree(msg_handle);\n}\n\n#define MAX_OPTION_SIZE 4096\n\nint tracecmd_msg_initial_setting(struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_msg msg;\n\tchar *p, *buf_end;\n\tssize_t buf_len;\n\tint pagesize;\n\tint options, i;\n\tint cpus;\n\tint ret;\n\n\tmemset(&msg, 0, sizeof(msg));\n\tret = tracecmd_msg_recv_wait(msg_handle->fd, &msg);\n\tif (ret < 0) {\n\t\tif (ret == -ETIMEDOUT)\n\t\t\ttracecmd_warning(\"Connection timed out\");\n\t\treturn ret;\n\t}\n\n\tif (ntohl(msg.hdr.cmd) != MSG_TINIT) {\n\t\tret = -EOPNOTSUPP;\n\t\tgoto error;\n\t}\n\n\tcpus = ntohl(msg.tinit.cpus);\n\ttracecmd_plog(\"cpus=%d\\n\", cpus);\n\tif (cpus < 0) {\n\t\tret = -EINVAL;\n\t\tgoto error;\n\t}\n\n\tmsg_handle->cpu_count = cpus;\n\n\tpagesize = ntohl(msg.tinit.page_size);\n\ttracecmd_plog(\"pagesize=%d\\n\", pagesize);\n\tif (pagesize <= 0) {\n\t\tret = -EINVAL;\n\t\tgoto error;\n\t}\n\n\tbuf_len = msg_buf_len(&msg);\n\tif (buf_len < 0) {\n\t\tret = -EINVAL;\n\t\tgoto error;\n\t}\n\n\tif (buf_len == 0)\n\t\tgoto no_options;\n\n\tif (msg.buf[buf_len-1] != '\\0') {\n\t\tret = -EINVAL;\n\t\tgoto error;\n\t}\n\n\tbuf_end = msg.buf + buf_len;\n\toptions = ntohl(msg.tinit.opt_num);\n\tfor (i = 0, p = msg.buf; i < options; i++, p++) {\n\t\tif (p >= buf_end) {\n\t\t\tret = -EINVAL;\n\t\t\tgoto error;\n\t\t}\n\n\t\t/* do we understand this option? */\n\t\tif (!process_option(msg_handle, p))\n\t\t\ttracecmd_plog(\"Cannot understand option '%s'\\n\", p);\n\n\t\tp = strchr(p, '\\0');\n\t}\n\nno_options:\n\tmsg_free(&msg);\n\treturn pagesize;\n\nerror:\n\terror_operation(&msg);\n\tif (ret == -EOPNOTSUPP)\n\t\thandle_unexpected_msg(msg_handle, &msg);\n\tmsg_free(&msg);\n\treturn ret;\n}\n\nint tracecmd_msg_send_port_array(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t unsigned int *ports)\n{\n\tstruct tracecmd_msg msg;\n\tint ret;\n\n\ttracecmd_msg_init(MSG_RINIT, &msg);\n\tret = make_rinit(&msg, msg_handle->cpu_count, ports);\n\tif (ret < 0)\n\t\treturn ret;\n\n\tret = tracecmd_msg_send(msg_handle, &msg);\n\tif (ret < 0)\n\t\treturn ret;\n\n\treturn 0;\n}\n\nint tracecmd_msg_send_close_msg(struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_msg msg;\n\n\ttracecmd_msg_init(MSG_CLOSE, &msg);\n\treturn tracecmd_msg_send(msg_handle, &msg);\n}\n\nint tracecmd_msg_send_close_resp_msg(struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_msg msg;\n\n\ttracecmd_msg_init(MSG_CLOSE_RESP, &msg);\n\treturn tracecmd_msg_send(msg_handle, &msg);\n}\n\nint tracecmd_msg_cont(struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_msg msg;\n\n\ttracecmd_msg_init(MSG_CONT, &msg);\n\treturn tracecmd_msg_send(msg_handle, &msg);\n}\n\nint tracecmd_msg_data_send(struct tracecmd_msg_handle *msg_handle,\n\t\t\t   const char *buf, int size)\n{\n\tstruct tracecmd_msg msg;\n\tint n;\n\tint ret;\n\tint count = 0;\n\n\t/* Don't bother doing anything if there's nothing to do */\n\tif (!size)\n\t\treturn 0;\n\n\ttracecmd_msg_init(MSG_SEND_DATA, &msg);\n\n\tmsg.buf = malloc(MSG_MAX_DATA_LEN);\n\tif (!msg.buf)\n\t\treturn -ENOMEM;\n\n\tmsg.hdr.size = htonl(MSG_MAX_LEN);\n\n\tn = size;\n\twhile (n) {\n\t\tif (n > MSG_MAX_DATA_LEN) {\n\t\t\tmemcpy(msg.buf, buf + count, MSG_MAX_DATA_LEN);\n\t\t\tn -= MSG_MAX_DATA_LEN;\n\t\t\tcount += MSG_MAX_DATA_LEN;\n\t\t} else {\n\t\t\tmsg.hdr.size = htonl(MSG_HDR_LEN + n);\n\t\t\tmemcpy(msg.buf, buf + count, n);\n\t\t\tn = 0;\n\t\t}\n\t\tret = msg_write(msg_handle, &msg);\n\t\tif (ret < 0)\n\t\t\tbreak;\n\t}\n\n\tmsg_free(&msg);\n\treturn ret;\n}\n\n/**\n * tracecmd_msg_send_options - Send options over the network\n * @msg_handle: message handle, holding the communication context\n * @handle: The output file that has the options to send\n *\n * Send options over the network. This is used when the output handle\n * has more options to send over the network after the trace. Some\n * options are sent before, and some sent afterward. Since the receiving\n * side needs to know the location to update the indexes, it will\n * handle the section header. This just sends out the raw content to\n * the receiver (requires that both sides have the same endianess, as\n * no conversion is made of the content of the options).\n *\n * Returns 0 on success and -1 on error.\n */\nint tracecmd_msg_send_options(struct tracecmd_msg_handle *msg_handle,\n\t\t\t      struct tracecmd_output *handle)\n{\n\tstruct tracecmd_msg msg;\n\tsize_t len;\n\tvoid *buf;\n\tint ret;\n\n\tbuf = tcmd_get_options(handle, &len);\n\tif (!buf)\n\t\treturn -1;\n\n\tret = tracecmd_msg_data_send(msg_handle, buf, len);\n\tfree(buf);\n\tif (ret < 0)\n\t\treturn ret;\n\n\ttracecmd_msg_init(MSG_FIN_DATA, &msg);\n\treturn tracecmd_msg_send(msg_handle, &msg);\n}\n\n/**\n * tracecmd_msg_flush_data - Send the current cache data over the network\n * @msg_handle: message handle, holding the communication context\n *\n * Send the content in the cache file over the nework, reset the file\n * and start the cache up again (with nothing in it).\n */\nint tracecmd_msg_flush_data(struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_msg msg;\n\tint ret;\n\n\tflush_cache(msg_handle);\n\ttracecmd_msg_init(MSG_FIN_DATA, &msg);\n\tret = tracecmd_msg_send(msg_handle, &msg);\n\tif (ret < 0)\n\t\treturn ret;\n\treturn tracecmd_msg_handle_cache(msg_handle);\n}\n\nint tracecmd_msg_finish_sending_data(struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_msg msg;\n\tint ret;\n\n\tflush_cache(msg_handle);\n\ttracecmd_msg_init(MSG_FIN_DATA, &msg);\n\tret = tracecmd_msg_send(msg_handle, &msg);\n\tif (ret < 0)\n\t\treturn ret;\n\treturn 0;\n}\n\nstatic int read_msg_data(struct tracecmd_msg_handle *msg_handle,\n\t\t\t struct tracecmd_msg *msg)\n{\n\tint cmd;\n\tint ret;\n\n\tret = tracecmd_msg_recv_wait(msg_handle->fd, msg);\n\tif (ret < 0) {\n\t\ttracecmd_warning(\"reading client %d (%s)\", ret, strerror(ret));\n\t\treturn ret;\n\t}\n\n\tcmd = ntohl(msg->hdr.cmd);\n\tif (cmd == MSG_FIN_DATA) {\n\t\t/* Finish receiving data */\n\t\treturn 0;\n\t} else if (cmd != MSG_SEND_DATA) {\n\t\tret = handle_unexpected_msg(msg_handle, msg);\n\t\tif (ret < 0)\n\t\t\treturn -1;\n\t\treturn 0;\n\t}\n\n\treturn msg_buf_len(msg);\n}\n\n/**\n * tracecmd_msg_read_options - Receive options from over the network\n * @msg_handle: message handle, holding the communication context\n * @handle: The output file to write the options to.\n *\n * Receive the options sent by tracecmd_msg_send_options().\n * See that function's documentation for mor details.\n *\n * Returns 0 on success and -1 on error.\n */\nint tracecmd_msg_read_options(struct tracecmd_msg_handle *msg_handle,\n\t\t\t      struct tracecmd_output *handle)\n{\n\tstruct tracecmd_msg msg;\n\tsize_t len = 0;\n\tvoid *buf = NULL;\n\tvoid *tmp;\n\tint ret;\n\tint n;\n\n\tmemset(&msg, 0, sizeof(msg));\n\twhile (!tracecmd_msg_done(msg_handle)) {\n\t\tn = read_msg_data(msg_handle, &msg);\n\t\tif (n <= 0)\n\t\t\tbreak;\n\n\t\ttmp = realloc(buf, n + len);\n\t\tif (!tmp)\n\t\t\tgoto error;\n\t\tbuf = tmp;\n\t\tmemcpy(buf + len, msg.buf, n);\n\t\tlen += n;\n\t\tmsg_free(&msg);\n\t}\n\tmsg_free(&msg);\n\n\tret = tcmd_append_options(handle, buf, len);\n\tfree(buf);\n\n\treturn ret;\n error:\n\tmsg_free(&msg);\n\tfree(buf);\n\treturn -1;\n}\n\nint tracecmd_msg_read_data(struct tracecmd_msg_handle *msg_handle, int ofd)\n{\n\tstruct tracecmd_msg msg;\n\tint t, n;\n\tssize_t s;\n\tint ret;\n\n\tmemset(&msg, 0, sizeof(msg));\n\n\twhile (!tracecmd_msg_done(msg_handle)) {\n\t\tn = read_msg_data(msg_handle, &msg);\n\t\tif (n <= 0)\n\t\t\tbreak;\n\t\tt = n;\n\t\ts = 0;\n\t\twhile (t > 0) {\n\t\t\ts = write(ofd, msg.buf+s, t);\n\t\t\tif (s < 0) {\n\t\t\t\tif (errno == EINTR)\n\t\t\t\t\tcontinue;\n\t\t\t\ttracecmd_warning(\"writing to file\");\n\t\t\t\tret = -errno;\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t\tt -= s;\n\t\t\ts = n - t;\n\t\t}\n\t\tmsg_free(&msg);\n\t}\n\tmsg_free(&msg);\n\n\treturn 0;\n\nerror:\n\terror_operation(&msg);\n\tmsg_free(&msg);\n\treturn ret;\n}\n\nint tracecmd_msg_collect_data(struct tracecmd_msg_handle *msg_handle, int ofd)\n{\n\tint ret;\n\n\tret = tracecmd_msg_read_data(msg_handle, ofd);\n\tif (ret)\n\t\treturn ret;\n\n\treturn tracecmd_msg_wait_close(msg_handle);\n}\n\nstatic int tracecmd_msg_wait_for_cmd(struct tracecmd_msg_handle *msg_handle, enum tracecmd_msg_cmd cmd)\n{\n\tstruct tracecmd_msg msg;\n\tint ret = -1;\n\n\tmemset(&msg, 0, sizeof(msg));\n\twhile (!tracecmd_msg_done(msg_handle)) {\n\t\tret = tracecmd_msg_recv(msg_handle->fd, &msg);\n\t\tif (ret < 0)\n\t\t\tgoto error;\n\n\t\tif (ntohl(msg.hdr.cmd) == cmd) {\n\t\t\tmsg_free(&msg);\n\t\t\treturn 0;\n\t\t}\n\n\t\terror_operation(&msg);\n\t\tret = handle_unexpected_msg(msg_handle, &msg);\n\t\tif (ret < 0)\n\t\t\tgoto error;\n\n\t\tmsg_free(&msg);\n\t}\n\nerror:\n\tmsg_free(&msg);\n\treturn ret;\n}\n\nint tracecmd_msg_wait(struct tracecmd_msg_handle *msg_handle)\n{\n\treturn tracecmd_msg_wait_for_cmd(msg_handle, MSG_CONT);\n}\n\nint tracecmd_msg_wait_close(struct tracecmd_msg_handle *msg_handle)\n{\n\treturn tracecmd_msg_wait_for_cmd(msg_handle, MSG_CLOSE);\n}\n\nint tracecmd_msg_wait_close_resp(struct tracecmd_msg_handle *msg_handle)\n{\n\treturn tracecmd_msg_wait_for_cmd(msg_handle, MSG_CLOSE_RESP);\n}\n\nstatic int make_trace_req_protos(char **buf, int *size,\n\t\t\t\t struct tracecmd_tsync_protos *protos)\n{\n\tint protos_size = 1;\n\tsize_t buf_size;\n\tchar **names;\n\tchar *nbuf;\n\tchar *p;\n\n\tnames = protos->names;\n\twhile (*names) {\n\t\tprotos_size += strlen(*names) + 1;\n\t\tnames++;\n\t}\n\n\tbuf_size = TRACE_REQ_PARAM_SIZE + protos_size;\n\tnbuf = realloc(*buf, *size + buf_size);\n\tif (!nbuf)\n\t\treturn -1;\n\n\tp = nbuf + *size;\n\tmemset(p, 0, buf_size);\n\n\t*(unsigned int *)p = htonl(TRACE_REQUEST_TSYNC_PROTOS);\n\tp += sizeof(int);\n\t*(unsigned int *)p = htonl(protos_size);\n\tp += sizeof(int);\n\n\tnames = protos->names;\n\twhile (*names) {\n\t\tstrcpy(p, *names);\n\t\tp += strlen(*names) + 1;\n\t\tnames++;\n\t}\n\tp = NULL;\n\n\t*size += buf_size;\n\t*buf = nbuf;\n\treturn 0;\n}\n\nstatic int make_trace_req_args(char **buf, int *size, int argc, char **argv)\n{\n\tsize_t args_size;\n\tsize_t buf_size;\n\tchar *nbuf;\n\tchar *p;\n\tint i;\n\n\targs_size = sizeof(int);\n\tfor (i = 0; i < argc; i++)\n\t\targs_size += strlen(argv[i]) + 1;\n\n\tbuf_size = TRACE_REQ_PARAM_SIZE + args_size;\n\tnbuf = realloc(*buf, *size + buf_size);\n\tif (!nbuf)\n\t\treturn -1;\n\n\tp = nbuf + *size;\n\tmemset(p, 0, buf_size);\n\n\t*(unsigned int *)p = htonl(TRACE_REQUEST_ARGS);\n\tp += sizeof(int);\n\t*(unsigned int *)p = htonl(args_size);\n\tp += sizeof(int);\n\n\t*(unsigned int *)p = htonl(argc);\n\tp += sizeof(int);\n\tfor (i = 0; i < argc; i++)\n\t\tp = stpcpy(p, argv[i]) + 1;\n\n\t*size += buf_size;\n\t*buf = nbuf;\n\treturn 0;\n}\n\nstatic int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,\n\t\t\t  bool use_fifos, unsigned long long trace_id,\n\t\t\t  struct tracecmd_tsync_protos *protos)\n{\n\tint size = 0;\n\tchar *buf = NULL;\n\n\tmsg->trace_req.flags = 0;\n\tif (use_fifos)\n\t\tmsg->trace_req.flags |= MSG_TRACE_USE_FIFOS;\n\tmsg->trace_req.flags = htonl(msg->trace_req.flags);\n\tmsg->trace_req.trace_id = htonll(trace_id);\n\n\tif (argc && argv)\n\t\tmake_trace_req_args(&buf, &size, argc, argv);\n\tif (protos && protos->names)\n\t\tmake_trace_req_protos(&buf, &size, protos);\n\n\tmsg->buf = buf;\n\tmsg->hdr.size = htonl(ntohl(msg->hdr.size) + size);\n\n\treturn size;\n}\n\nint tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tint argc, char **argv, bool use_fifos,\n\t\t\t\tunsigned long long trace_id,\n\t\t\t\tstruct tracecmd_tsync_protos *protos)\n{\n\tstruct tracecmd_msg msg;\n\tint ret;\n\n\ttracecmd_msg_init(MSG_TRACE_REQ, &msg);\n\tret = make_trace_req(&msg, argc, argv, use_fifos, trace_id, protos);\n\tif (ret < 0)\n\t\treturn ret;\n\n\treturn tracecmd_msg_send(msg_handle, &msg);\n}\n\nint tracecmd_msg_send_trace_proxy(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t  int argc, char **argv, bool use_fifos,\n\t\t\t\t  unsigned long long trace_id,\n\t\t\t\t  struct tracecmd_tsync_protos *protos,\n\t\t\t\t  unsigned int nr_cpus,\n\t\t\t\t  unsigned int siblings)\n{\n\tstruct tracecmd_msg msg;\n\tint ret;\n\n\ttracecmd_msg_init(MSG_TRACE_PROXY, &msg);\n\tret = make_trace_req(&msg, argc, argv, use_fifos, trace_id, protos);\n\tif (ret < 0)\n\t\treturn ret;\n\n\tmsg.trace_proxy.cpus = htonl(nr_cpus);\n\tmsg.trace_proxy.siblings = htonl(siblings);\n\treturn tracecmd_msg_send(msg_handle, &msg);\n}\n\nstatic int get_trace_req_protos(char *buf, int length,\n\t\t\t\tstruct tracecmd_tsync_protos **protos)\n{\n\tstruct tracecmd_tsync_protos *plist = NULL;\n\tint count = 0;\n\tchar *p;\n\tint i, j;\n\n\ti = length;\n\tp = buf;\n\twhile (i > 0) {\n\t\ti -= strlen(p) + 1;\n\t\tcount++;\n\t\tp += strlen(p) + 1;\n\t}\n\n\tplist = calloc(1, sizeof(struct tracecmd_tsync_protos));\n\tif (!plist)\n\t\tgoto error;\n\tplist->names = calloc(count + 1, sizeof(char *));\n\tif (!plist->names)\n\t\tgoto error;\n\ti = length;\n\tp = buf;\n\tj = 0;\n\twhile (i > 0 && j < (count - 1)) {\n\t\ti -= strlen(p) + 1;\n\t\tplist->names[j++] = strdup(p);\n\t\tp += strlen(p) + 1;\n\t}\n\n\t*protos = plist;\n\treturn 0;\nerror:\n\tif (plist) {\n\t\tfree(plist->names);\n\t\tfree(plist);\n\t}\n\treturn -1;\n}\n\nstatic int get_trace_req_args(char *buf, int length, int *argc, char ***argv)\n{\n\tunsigned int nr_args;\n\tchar *p = NULL, *buf_end;\n\tchar **args = NULL;\n\tint ret;\n\tint i;\n\n\tif (length <= sizeof(int) || buf[length - 1] != '\\0') {\n\t\tret = -EINVAL;\n\t\tgoto out;\n\t}\n\n\tnr_args = ntohl(*(unsigned int *)buf);\n\tbuf += sizeof(int);\n\tlength -= sizeof(int);\n\n\targs = calloc(nr_args, sizeof(*args));\n\tif (!args) {\n\t\tret = -ENOMEM;\n\t\tgoto out;\n\t}\n\n\tp = malloc(length);\n\tif (!p) {\n\t\tret = -ENOMEM;\n\t\tgoto out;\n\t}\n\tmemcpy(p, buf, length);\n\n\tbuf_end = p + length;\n\tfor (i = 0; i < nr_args; i++, p++) {\n\t\tif (p >= buf_end) {\n\t\t\tret = -EINVAL;\n\t\t\tgoto out;\n\t\t}\n\t\targs[i] = p;\n\t\tp = strchr(p, '\\0');\n\t}\n\n\t*argc = nr_args;\n\t*argv = args;\n\treturn 0;\n\nout:\n\tfree(p);\n\tfree(args);\n\treturn ret;\n\n}\n\nstatic int msg_recv_trace_req_proxy(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t    int *argc, char ***argv, bool *use_fifos,\n\t\t\t\t    unsigned long long *trace_id,\n\t\t\t\t    struct tracecmd_tsync_protos **protos,\n\t\t\t\t    unsigned int *cpus,\n\t\t\t\t    unsigned int *siblings)\n{\n\tstruct tracecmd_msg msg;\n\tunsigned int param_id;\n\tint param_length;\n\tssize_t buf_len;\n\tchar *p;\n\tint ret;\n\n\tret = tracecmd_msg_recv(msg_handle->fd, &msg);\n\tif (ret < 0)\n\t\treturn ret;\n\n\tswitch (ntohl(msg.hdr.cmd)) {\n\tcase MSG_TRACE_PROXY:\n\t\tif (cpus)\n\t\t\t*cpus = ntohl(msg.trace_proxy.cpus);\n\t\tif (siblings)\n\t\t\t*siblings = ntohl(msg.trace_proxy.siblings);\n\t\t/* fall through */\n\tcase MSG_TRACE_REQ:\n\t\tbreak;\n\tdefault:\n\t\tret = -ENOTSUP;\n\t\tgoto out;\n\t}\n\n\tbuf_len = ntohl(msg.hdr.size) - MSG_HDR_LEN - ntohl(msg.hdr.cmd_size);\n\tif (buf_len < 0) {\n\t\tret = -EINVAL;\n\t\tgoto out;\n\t}\n\n\t*use_fifos = ntohl(msg.trace_req.flags) & MSG_TRACE_USE_FIFOS;\n\t*trace_id = ntohll(msg.trace_req.trace_id);\n\tp = msg.buf;\n\twhile (buf_len > 2 * sizeof(int)) {\n\t\tparam_id = ntohl(*((unsigned int *)p));\n\t\tp += sizeof(int);\n\t\tbuf_len -= sizeof(int);\n\t\tparam_length = ntohl(*((unsigned int *)p));\n\t\tp += sizeof(int);\n\t\tbuf_len -= sizeof(int);\n\t\tif (buf_len < param_length)\n\t\t\tbreak;\n\t\tret = 0;\n\t\tswitch (param_id) {\n\t\tcase TRACE_REQUEST_ARGS:\n\t\t\tret = get_trace_req_args(p, param_length, argc, argv);\n\t\t\tbreak;\n\t\tcase TRACE_REQUEST_TSYNC_PROTOS:\n\t\t\tret = get_trace_req_protos(p, param_length, protos);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tif (ret)\n\t\t\tbreak;\n\t\tbuf_len -= param_length;\n\t\tp += param_length;\n\t}\n\n\tmsg_free(&msg);\n\treturn 0;\n\nout:\n\terror_operation(&msg);\n\tif (ret == -EOPNOTSUPP)\n\t\thandle_unexpected_msg(msg_handle, &msg);\n\tmsg_free(&msg);\n\treturn ret;\n}\n\n/*\n * NOTE: On success, the returned `argv` should be freed with:\n *     free(argv[0]);\n *     free(argv);\n * and `tsync_protos` with free(tsync_protos);\n */\nint tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tint *argc, char ***argv, bool *use_fifos,\n\t\t\t\tunsigned long long *trace_id,\n\t\t\t\tstruct tracecmd_tsync_protos **protos)\n{\n\treturn msg_recv_trace_req_proxy(msg_handle, argc, argv, use_fifos,\n\t\t\t\t\ttrace_id, protos, NULL, NULL);\n}\n\n/*\n * NOTE: On success, the returned `argv` should be freed with:\n *     free(argv[0]);\n *     free(argv);\n * and `tsync_protos` with free(tsync_protos);\n */\nint tracecmd_msg_recv_trace_proxy(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t  int *argc, char ***argv, bool *use_fifos,\n\t\t\t\t  unsigned long long *trace_id,\n\t\t\t\t  struct tracecmd_tsync_protos **protos,\n\t\t\t\t  unsigned int *cpus, unsigned int *siblings)\n{\n\treturn msg_recv_trace_req_proxy(msg_handle, argc, argv, use_fifos,\n\t\t\t\t\ttrace_id, protos, cpus, siblings);\n}\n\n/**\n * tracecmd_msg_send_time_sync - Send a time sync packet\n * @msg_handle: message handle, holding the communication context\n * @sync_protocol: name of the time synch protocol, string up to\n *\t\t   TRACECMD_TSYNC_PNAME_LENGTH characters length.\n * @sync_msg_id: id if the time synch message, protocol dependent\n * @payload_size: size of the packet payload, 0 in case of no payload\n * @payload: pointer to the packet payload, or NULL in case of no payload\n *\n * Returns 0 if packet is sent successfully, or negative error otherwise.\n */\nint tracecmd_msg_send_time_sync(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tchar *sync_protocol, unsigned int sync_msg_id,\n\t\t\t\tunsigned int payload_size, char *payload)\n{\n\tstruct tracecmd_msg msg;\n\n\ttracecmd_msg_init(MSG_TIME_SYNC, &msg);\n\tstrncpy(msg.tsync.sync_protocol_name, sync_protocol, TRACECMD_TSYNC_PNAME_LENGTH);\n\tmsg.tsync.sync_msg_id = htonl(sync_msg_id);\n\tmsg.hdr.size = htonl(ntohl(msg.hdr.size) + payload_size);\n\n\tmsg.buf = payload;\n\treturn msg_send_nofree(msg_handle, &msg);\n}\n\n/**\n * tracecmd_msg_recv_time_sync - Receive a time sync packet\n * @msg_handle: message handle, holding the communication context\n * @sync_protocol: return the name of the packet's time synch protocol.\n *\t\t   It must point to a prealocated buffer with size\n *\t\t   TRACECMD_TSYNC_PNAME_LENGTH\n * @sync_msg_id: return the id of the packet's time synch message\n * @payload_size: size of the packet's payload, can be:\n *\t\t NULL - the payload is not interested and should be ignored\n *\t\t pointer to int, with value 0 - update with the size of the payload\n *\t\t\t\t\t\tallocate memory and cpy the payload\n *\t\t\t\t\t\tinto it\n *\t\t pointer to int, with value greater than 0 - expected size of the\n *\t\t\t\t\t\t\t     payload, preallocated\n *\t\t\t\t\t\t\t     memory is passed to the API\n *\t\t\t\t\t\t\t     with that size\n *@payload: pointer to the packet payload, can be:\n *\t     NULL - the payload is not interested and should be ignored\n *\t     pointer to char *, with value NULL - a new memory is allocated and returned\n *\t\t\t\t\t\t  here, containing the packet's payload\n *\t\t\t\t\t\t  the @payload_size is updated with the\n *\t\t\t\t\t\t  size of the allocated memory. It must be\n *\t\t\t\t\t\t  freed by free()\n *\t     pointer to char *, with no-NULL value - A prealocated array is passed, with size\n *\t\t\t\t\t\t     @payload_size. If payload's size is equal\n *\t\t\t\t\t\t     or less, it will be copied here.\n *\n * Returns 0 if packet is received successfully, or negative error otherwise.\n */\nint tracecmd_msg_recv_time_sync(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\tchar *sync_protocol,\n\t\t\t\tunsigned int *sync_msg_id,\n\t\t\t\tunsigned int *payload_size, char **payload)\n{\n\tstruct tracecmd_msg msg;\n\tint ret = -1;\n\tint buf_size;\n\n\tmemset(&msg, 0, sizeof(msg));\n\tret = tracecmd_msg_recv(msg_handle->fd, &msg);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tif (ntohl(msg.hdr.cmd) != MSG_TIME_SYNC) {\n\t\tret = -EOPNOTSUPP;\n\t\tgoto out;\n\t}\n\n\tif (sync_protocol)\n\t\tstrncpy(sync_protocol, msg.tsync.sync_protocol_name,\n\t\t\t\tTRACECMD_TSYNC_PNAME_LENGTH);\n\tif (sync_msg_id)\n\t\t*sync_msg_id = ntohl(msg.tsync.sync_msg_id);\n\n\tbuf_size = msg_buf_len(&msg);\n\tif (buf_size < 0) {\n\t\tret = -EINVAL;\n\t\tgoto out;\n\t}\n\n\tif (buf_size && payload && payload_size) {\n\t\tif (*payload_size) {\n\t\t\tif (*payload_size < buf_size || *payload == NULL) {\n\t\t\t\tret = -ENOMEM;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tmemcpy(*payload, msg.buf, buf_size);\n\t\t\tgoto out;\n\t\t}\n\n\t\t*payload = malloc(buf_size);\n\t\tif (*payload == NULL) {\n\t\t\tret = -ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\t\t*payload_size = buf_size;\n\t\tmemcpy(*payload, msg.buf, buf_size);\n\t}\n\nout:\n\tmsg_free(&msg);\n\treturn ret;\n}\n\nstatic int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,\n\t\t\t   unsigned int *ports, bool use_fifos,\n\t\t\t   unsigned long long trace_id,\n\t\t\t   const char *tsync_proto,\n\t\t\t   unsigned int tsync_port)\n{\n\tint data_size;\n\n\tif (!tsync_proto)\n\t\ttsync_proto = \"\";\n\n\tdata_size = write_uints(NULL, 0, ports, nr_cpus);\n\tmsg->buf = malloc(data_size);\n\tif (!msg->buf)\n\t\treturn -ENOMEM;\n\twrite_uints(msg->buf, data_size, ports, nr_cpus);\n\n\tmsg->hdr.size = htonl(ntohl(msg->hdr.size) + data_size);\n\tmsg->trace_resp.flags = use_fifos ? MSG_TRACE_USE_FIFOS : 0;\n\tmsg->trace_resp.flags = htonl(msg->trace_resp.flags);\n\tstrncpy(msg->trace_resp.tsync_proto_name, tsync_proto, TRACECMD_TSYNC_PNAME_LENGTH);\n\tmsg->trace_resp.tsync_port = htonl(tsync_port);\n\n\tmsg->trace_resp.cpus = htonl(nr_cpus);\n\tmsg->trace_resp.page_size = htonl(page_size);\n\tmsg->trace_resp.trace_id = htonll(trace_id);\n\n\treturn 0;\n}\n\nint tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t int nr_cpus, int page_size,\n\t\t\t\t unsigned int *ports, bool use_fifos,\n\t\t\t\t unsigned long long trace_id,\n\t\t\t\t const char *tsync_proto, unsigned int tsync_port)\n{\n\tstruct tracecmd_msg msg;\n\tint ret;\n\n\ttracecmd_msg_init(MSG_TRACE_RESP, &msg);\n\tret = make_trace_resp(&msg, page_size, nr_cpus, ports,\n\t\t\t      use_fifos, trace_id, tsync_proto, tsync_port);\n\tif (ret < 0)\n\t\treturn ret;\n\n\treturn tracecmd_msg_send(msg_handle, &msg);\n}\n\nint tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t int *nr_cpus, int *page_size,\n\t\t\t\t unsigned int **ports, bool *use_fifos,\n\t\t\t\t unsigned long long *trace_id,\n\t\t\t\t char **tsync_proto,\n\t\t\t\t unsigned int *tsync_port)\n{\n\tstruct tracecmd_msg msg;\n\tchar *p, *buf_end;\n\tssize_t buf_len;\n\tint i, ret;\n\n\tret = tracecmd_msg_recv(msg_handle->fd, &msg);\n\tif (ret < 0)\n\t\treturn ret;\n\n\tif (ntohl(msg.hdr.cmd) != MSG_TRACE_RESP) {\n\t\tret = -ENOTSUP;\n\t\tgoto out;\n\t}\n\n\tbuf_len = msg_buf_len(&msg);\n\tif (buf_len <= 0) {\n\t\tret = -EINVAL;\n\t\tgoto out;\n\t}\n\n\t*use_fifos = ntohl(msg.trace_resp.flags) & MSG_TRACE_USE_FIFOS;\n\t*nr_cpus = ntohl(msg.trace_resp.cpus);\n\t*page_size = ntohl(msg.trace_resp.page_size);\n\t*trace_id = ntohll(msg.trace_resp.trace_id);\n\t*tsync_proto = strdup(msg.trace_resp.tsync_proto_name);\n\t*tsync_port = ntohl(msg.trace_resp.tsync_port);\n\t*ports = calloc(*nr_cpus, sizeof(**ports));\n\tif (!*ports) {\n\t\tret = -ENOMEM;\n\t\tgoto out;\n\t}\n\n\tbuf_end = msg.buf + buf_len;\n\tfor (i = 0, p = msg.buf; i < *nr_cpus; i++, p++) {\n\t\tif (p >= buf_end || tatou(p, &(*ports)[i])) {\n\t\t\tfree(*ports);\n\t\t\tret = -EINVAL;\n\t\t\tgoto out;\n\t\t}\n\t\tp = strchr(p, '\\0');\n\t}\n\n\tmsg_free(&msg);\n\treturn 0;\n\nout:\n\terror_operation(&msg);\n\tif (ret == -EOPNOTSUPP)\n\t\thandle_unexpected_msg(msg_handle, &msg);\n\tmsg_free(&msg);\n\treturn ret;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-output.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <stdarg.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <errno.h>\n#include <glob.h>\n\n#include \"tracefs.h\"\n#include \"trace-cmd.h\"\n#include \"trace-cmd-local.h\"\n#include \"trace-write-local.h\"\n#include \"list.h\"\n#include \"trace-msg.h\"\n\n/* We can't depend on the host size for size_t, all must be 64 bit */\ntypedef unsigned long long\ttsize_t;\ntypedef long long\t\tstsize_t;\n\nstruct tracecmd_option {\n\tunsigned short\tid;\n\tint\t\tsize;\n\tvoid\t\t*data;\n\ttsize_t\t\toffset;\n\tstruct list_head list;\n};\n\nstruct tracecmd_buffer {\n\tint\t\t\t\tcpus;\n\tvoid\t\t\t\t*name;\n\ttsize_t\t\t\t\toffset;\n\tstruct tracecmd_option\t\t*option;\n\tstruct list_head\t\tlist;\n};\n\nenum {\n\tOUTPUT_FL_SEND_META\t= (1 << 0),\n};\n\nstruct tracecmd_output {\n\tint\t\t\tfd;\n\tint\t\t\tpage_size;\n\tint\t\t\tcpus;\n\tstruct tep_handle\t*pevent;\n\tchar\t\t\t*tracing_dir;\n\tchar\t\t\t*kallsyms;\n\tint\t\t\tnr_options;\n\tbool\t\t\tquiet;\n\tunsigned long\t\tfile_state;\n\tunsigned long\t\tfile_version;\n\n\t/* size of meta-data strings, not yet stored in the file */\n\tunsigned long\t\tstrings_p;\n\t/* current virtual offset of meta-data string */\n\tunsigned long\t\tstrings_offs;\n\n\tunsigned long long\toptions_start;\n\tunsigned long long\toptions_next;\n\tbool\t\t\tbig_endian;\n\tbool\t\t\tdo_compress;\n\tstruct tracecmd_compression *compress;\n\n\tstruct list_head\toptions;\n\tstruct list_head\tbuffers;\n\tstruct tracecmd_msg_handle *msg_handle;\n\tchar\t\t\t*trace_clock;\n\n\t/* meta-data strings, not yet stored in the file */\n\tchar\t\t\t*strings;\n};\n\nstruct list_event {\n\tstruct list_event\t\t*next;\n\tchar\t\t\t\t*name;\n\tchar\t\t\t\t*file;\n};\n\nstruct list_event_system {\n\tstruct list_event_system\t*next;\n\tstruct list_event\t\t*events;\n\tchar\t\t\t\t*name;\n};\n\n#define HAS_SECTIONS(H) ((H)->file_version >= FILE_VERSION_SECTIONS)\n\nstatic int write_options(struct tracecmd_output *handle);\nstatic int save_string_section(struct tracecmd_output *handle);\n\n__hidden long long\ntcmd_do_write_check(struct tracecmd_output *handle, const void *data, long long size)\n{\n\tif (handle->do_compress)\n\t\treturn tracecmd_compress_buffer_write(handle->compress, data, size);\n\n\tif (handle->msg_handle)\n\t\treturn tracecmd_msg_data_send(handle->msg_handle, data, size);\n\n\treturn __do_write_check(handle->fd, data, size);\n}\n\nstatic inline off_t do_lseek(struct tracecmd_output *handle, off_t offset, int whence)\n{\n\tif (handle->do_compress)\n\t\treturn tracecmd_compress_lseek(handle->compress, offset, whence);\n\n\tif (handle->msg_handle)\n\t\treturn tcmd_msg_lseek(handle->msg_handle, offset, whence);\n\n\treturn lseek(handle->fd, offset, whence);\n}\n\nstatic inline int do_preed(struct tracecmd_output *handle, void *dst, int len, off_t offset)\n{\n\tif (handle->do_compress)\n\t\treturn tracecmd_compress_pread(handle->compress, dst, len, offset);\n\n\treturn pread(handle->fd, dst, len, offset);\n}\n\nstatic short convert_endian_2(struct tracecmd_output *handle, short val)\n{\n\tif (!handle->pevent)\n\t\treturn val;\n\n\treturn tep_read_number(handle->pevent, &val, 2);\n}\n\nstatic int convert_endian_4(struct tracecmd_output *handle, int val)\n{\n\tif (!handle->pevent)\n\t\treturn val;\n\n\treturn tep_read_number(handle->pevent, &val, 4);\n}\n\nstatic unsigned long long convert_endian_8(struct tracecmd_output *handle,\n\t\t\t\t\t   unsigned long long val)\n{\n\tif (!handle->pevent)\n\t\treturn val;\n\n\treturn tep_read_number(handle->pevent, &val, 8);\n}\n\n__hidden void tcmd_out_compression_reset(struct tracecmd_output *handle)\n{\n\tif (!handle->compress)\n\t\treturn;\n\n\ttracecmd_compress_reset(handle->compress);\n\thandle->do_compress = false;\n}\n\n__hidden int tcmd_out_uncompress_block(struct tracecmd_output *handle)\n{\n\tint ret = 0;\n\n\tif (!handle->compress)\n\t\treturn 0;\n\n\tret = tracecmd_uncompress_block(handle->compress);\n\tif (!ret)\n\t\thandle->do_compress = true;\n\n\treturn ret;\n}\n\n__hidden int tcmd_out_compression_start(struct tracecmd_output *handle)\n{\n\tif (!handle->compress)\n\t\treturn 0;\n\n\ttracecmd_compress_reset(handle->compress);\n\thandle->do_compress = true;\n\n\treturn 0;\n}\n\n__hidden int tcmd_out_compression_end(struct tracecmd_output *handle)\n{\n\tif (!handle->compress)\n\t\treturn 0;\n\n\thandle->do_compress = false;\n\treturn tracecmd_compress_block(handle->compress);\n}\n\nstatic long add_string(struct tracecmd_output *handle, const char *string)\n{\n\tint size = strlen(string) + 1;\n\tint pos = handle->strings_p;\n\tchar *strings;\n\n\tstrings = realloc(handle->strings, pos + size);\n\tif (!strings)\n\t\treturn -1;\n\thandle->strings = strings;\n\tmemcpy(handle->strings + pos, string, size);\n\thandle->strings_p += size;\n\n\treturn handle->strings_offs + pos;\n}\n\n/**\n * tracecmd_set_quiet - Set if to print output to the screen\n * @quiet: If non zero, print no output to the screen\n *\n */\nvoid tracecmd_set_quiet(struct tracecmd_output *handle, bool set_quiet)\n{\n\tif (handle)\n\t\thandle->quiet = set_quiet;\n}\n\nvoid tracecmd_set_out_clock(struct tracecmd_output *handle, const char *clock)\n{\n\tif (handle && clock) {\n\t\tfree(handle->trace_clock);\n\t\thandle->trace_clock = strdup(clock);\n\t}\n}\n\n/**\n * tracecmd_get_quiet - Get if to print output to the screen\n * Returns non zero, if no output to the screen should be printed\n *\n */\nbool tracecmd_get_quiet(struct tracecmd_output *handle)\n{\n\tif (handle)\n\t\treturn handle->quiet;\n\treturn false;\n}\n\nvoid tracecmd_output_free(struct tracecmd_output *handle)\n{\n\tstruct tracecmd_option *option;\n\tstruct tracecmd_buffer *buffer;\n\n\tif (!handle)\n\t\treturn;\n\n\tif (handle->tracing_dir)\n\t\tfree(handle->tracing_dir);\n\n\tif (handle->pevent)\n\t\ttep_unref(handle->pevent);\n\n\twhile (!list_empty(&handle->buffers)) {\n\t\tbuffer = container_of(handle->buffers.next,\n\t\t\t\t      struct tracecmd_buffer, list);\n\t\tlist_del(&buffer->list);\n\t\tfree(buffer->name);\n\t\tfree(buffer);\n\t}\n\twhile (!list_empty(&handle->options)) {\n\t\toption = container_of(handle->options.next,\n\t\t\t\t      struct tracecmd_option, list);\n\t\tlist_del(&option->list);\n\t\tfree(option->data);\n\t\tfree(option);\n\t}\n\n\tfree(handle->strings);\n\tfree(handle->trace_clock);\n\ttracecmd_compress_destroy(handle->compress);\n\tfree(handle);\n}\n\nvoid tracecmd_output_flush(struct tracecmd_output *handle)\n{\n\tif (!handle)\n\t\treturn;\n\n\tif (HAS_SECTIONS(handle)) {\n\t\t/* write any unsaved options at the end of trace files with sections */\n\t\twrite_options(handle);\n\n\t\t/* write strings section */\n\t\tsave_string_section(handle);\n\t}\n}\n\nvoid tracecmd_output_close(struct tracecmd_output *handle)\n{\n\tif (!handle)\n\t\treturn;\n\n\ttracecmd_output_flush(handle);\n\n\tif (handle->fd >= 0) {\n\t\tclose(handle->fd);\n\t\thandle->fd = -1;\n\t}\n\n\ttracecmd_output_free(handle);\n}\nstatic unsigned long get_size_fd(int fd)\n{\n\tunsigned long long size = 0;\n\tchar buf[BUFSIZ];\n\tint r;\n\n\tdo {\n\t\tr = read(fd, buf, BUFSIZ);\n\t\tif (r > 0)\n\t\t\tsize += r;\n\t} while (r > 0);\n\n\tlseek(fd, 0, SEEK_SET);\n\n\treturn size;\n}\n\nstatic unsigned long get_size(const char *file)\n{\n\tunsigned long long size = 0;\n\tint fd;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0) {\n\t\ttracecmd_warning(\"Can't read '%s'\", file);\n\t\treturn 0; /* Caller will fail with zero */\n\t}\n\tsize = get_size_fd(fd);\n\tclose(fd);\n\n\treturn size;\n}\n\nstatic tsize_t copy_file_fd(struct tracecmd_output *handle, int fd, unsigned long long max)\n{\n\ttsize_t rsize = BUFSIZ;\n\ttsize_t size = 0;\n\tchar buf[BUFSIZ];\n\tstsize_t r;\n\n\tdo {\n\t\tif (max && rsize > max)\n\t\t\trsize = max;\n\n\t\tr = read(fd, buf, rsize);\n\t\tif (r > 0) {\n\t\t\tsize += r;\n\t\t\tif (tcmd_do_write_check(handle, buf, r))\n\t\t\t\treturn 0;\n\t\t\tif (max) {\n\t\t\t\tmax -= r;\n\t\t\t\tif (!max)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} while (r > 0);\n\n\treturn size;\n}\n\nstatic tsize_t copy_file(struct tracecmd_output *handle,\n\t\t\t\t    const char *file)\n{\n\ttsize_t size = 0;\n\tint fd;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0) {\n\t\ttracecmd_warning(\"Can't read '%s'\", file);\n\t\treturn 0;\n\t}\n\tsize = copy_file_fd(handle, fd, 0);\n\tclose(fd);\n\n\treturn size;\n}\n\n#define PAGES_IN_CHUNK 10\n__hidden unsigned long long\ntcmd_out_copy_fd_compress(struct tracecmd_output *handle, int fd,\n\t\t\t  unsigned long long max, unsigned long long *write_size,\n\t\t\t  int page)\n{\n\tsize_t rsize = 0;\n\tsize_t wsize = 0;\n\tsize_t size;\n\tint ret;\n\n\tif (handle->compress) {\n\t\trsize = max;\n\t\tret = tracecmd_compress_copy_from(handle->compress, fd,\n\t\t\t\t\t\t  PAGES_IN_CHUNK * page,\n\t\t\t\t\t\t  &rsize, &wsize);\n\t\tif (ret < 0)\n\t\t\treturn 0;\n\n\t\tsize = rsize;\n\t\tif (write_size)\n\t\t\t*write_size = wsize;\n\t} else {\n\t\tsize = copy_file_fd(handle, fd, max);\n\t\tif (write_size)\n\t\t\t*write_size = size;\n\t}\n\n\treturn size;\n}\n\nstatic tsize_t copy_file_compress(struct tracecmd_output *handle,\n\t\t\t\t  const char *file, unsigned long long *write_size)\n{\n\tint ret;\n\tint fd;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0) {\n\t\ttracecmd_warning(\"Can't read '%s'\", file);\n\t\treturn 0;\n\t}\n\n\tret = tcmd_out_copy_fd_compress(handle, fd, 0, write_size, getpagesize());\n\tif (!ret)\n\t\ttracecmd_warning(\"Can't compress '%s'\", file);\n\n\tclose(fd);\n\treturn ret;\n}\n\n/*\n * Finds the path to the debugfs/tracing\n * Allocates the string and stores it.\n */\nstatic const char *find_tracing_dir(struct tracecmd_output *handle)\n{\n\tif (!handle->tracing_dir) {\n\t\tconst char *dir = tracefs_tracing_dir();\n\n\t\tif (dir)\n\t\t\thandle->tracing_dir = strdup(dir);\n\t}\n\treturn handle->tracing_dir;\n}\n\nstatic char *get_tracing_file(struct tracecmd_output *handle, const char *name)\n{\n\tconst char *tracing;\n\tchar *file;\n\tint ret;\n\n\ttracing = find_tracing_dir(handle);\n\tif (!tracing)\n\t\treturn NULL;\n\n\tret = asprintf(&file, \"%s/%s\", tracing, name);\n\tif (ret < 0)\n\t\treturn NULL;\n\n\treturn file;\n}\n\nstatic void put_tracing_file(char *file)\n{\n\tfree(file);\n}\n\nint tracecmd_ftrace_enable(int set)\n{\n\tstruct stat buf;\n\tchar *path = \"/proc/sys/kernel/ftrace_enabled\";\n\tint fd;\n\tchar *val = set ? \"1\" : \"0\";\n\tint ret = 0;\n\n\t/* if ftace_enable does not exist, simply ignore it */\n\tfd = stat(path, &buf);\n\tif (fd < 0)\n\t\treturn ENODEV;\n\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0) {\n\t\ttracecmd_warning(\"Can't %s ftrace\", set ? \"enable\" : \"disable\");\n\t\treturn EIO;\n\t}\n\n\tif (write(fd, val, 1) < 0)\n\t\tret = -1;\n\tclose(fd);\n\n\treturn ret;\n}\n\n__hidden unsigned long long\ntcmd_out_write_section_header(struct tracecmd_output *handle, unsigned short header_id,\n\t\t\t      char *description, int flags, bool option)\n{\n\ttsize_t endian8;\n\ttsize_t offset;\n\tlong long size;\n\tshort endian2;\n\tint endian4;\n\tint desc;\n\n\tif (header_id >= TRACECMD_OPTION_MAX)\n\t\treturn -1;\n\tif (!HAS_SECTIONS(handle))\n\t\treturn 0;\n\tif (!handle->compress)\n\t\tflags &= ~TRACECMD_SEC_FL_COMPRESS;\n\toffset = do_lseek(handle, 0, SEEK_CUR);\n\tif (option) {\n\t\tendian8 = convert_endian_8(handle, offset);\n\t\tif (!tracecmd_add_option(handle, header_id, 8, &endian8))\n\t\t\treturn -1;\n\t}\n\t/* Section ID */\n\tendian2 = convert_endian_2(handle, header_id);\n\tif (tcmd_do_write_check(handle, &endian2, 2))\n\t\treturn (off_t)-1;\n\n\t/* Section flags */\n\tendian2 = convert_endian_2(handle, flags);\n\tif (tcmd_do_write_check(handle, &endian2, 2))\n\t\treturn (off_t)-1;\n\n\t/* Section description */\n\tif (description)\n\t\tdesc = add_string(handle, description);\n\telse\n\t\tdesc = -1;\n\tendian4 = convert_endian_4(handle, desc);\n\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\treturn (off_t)-1;\n\n\toffset = do_lseek(handle, 0, SEEK_CUR);\n\tsize = 0;\n\t/* Reserve for section size */\n\tif (tcmd_do_write_check(handle, &size, 8))\n\t\treturn (off_t)-1;\n\treturn offset;\n}\n\nstatic unsigned long long\nwrite_compress_section_header(struct tracecmd_output *handle, unsigned short header_id,\n\t\t\t      char *description, bool option)\n{\n\treturn tcmd_out_write_section_header(handle, header_id, description,\n\t\t\t\t\t     TRACECMD_SEC_FL_COMPRESS, option);\n}\n\n__hidden int tcmd_out_update_section_header(struct tracecmd_output *handle, tsize_t offset)\n{\n\ttsize_t current;\n\ttsize_t endian8;\n\ttsize_t size;\n\n\tif (!HAS_SECTIONS(handle) || offset == 0)\n\t\treturn 0;\n\n\tcurrent = do_lseek(handle, 0, SEEK_CUR);\n\t/* The real size is the difference between the saved offset and\n\t * the current offset - 8 bytes, the reserved space for the section size.\n\t */\n\tsize = current - offset;\n\tif (size < 8)\n\t\treturn -1;\n\tsize -= 8;\n\tif (do_lseek(handle, offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\tendian8 = convert_endian_8(handle, size);\n\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\treturn -1;\n\tif (do_lseek(handle, current, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\treturn 0;\n}\n\n/*\n * For files that can change while reading them (like /proc/kallsyms) the\n * normal \"size != check_size\" can fail. That's because from the time the file\n * size is determined and written into the output to the time the actual data\n * is copied into the output, the size can change. In this case, the size that\n * was stored in the output needs to be updated.\n */\nstatic int update_endian_4(struct tracecmd_output *handle,\n\t\t\t   off_t offset, int val)\n{\n\ttsize_t current;\n\tint endian4;\n\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\tcurrent = do_lseek(handle, 0, SEEK_CUR);\n\tif (current == (off_t)-1)\n\t\treturn -1;\n\n\tif (do_lseek(handle, offset, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\tendian4 = convert_endian_4(handle, val);\n\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\treturn -1;\n\n\tif (do_lseek(handle, current, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic int save_string_section(struct tracecmd_output *handle)\n{\n\ttsize_t offset;\n\n\tif (!handle->strings || !handle->strings_p)\n\t\treturn 0;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_OPTION_STRINGS)) {\n\t\ttracecmd_warning(\"Cannot write strings, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_STRINGS, \"strings\", false);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\ttcmd_out_compression_start(handle);\n\n\tif (tcmd_do_write_check(handle, handle->strings, handle->strings_p))\n\t\tgoto error;\n\n\tif (tcmd_out_compression_end(handle))\n\t\tgoto error;\n\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\treturn -1;\n\n\thandle->strings_offs += handle->strings_p;\n\tfree(handle->strings);\n\thandle->strings = NULL;\n\thandle->strings_p = 0;\n\thandle->file_state = TRACECMD_OPTION_STRINGS;\n\treturn 0;\n\nerror:\n\ttcmd_out_compression_reset(handle);\n\treturn -1;\n}\n\nstatic int read_header_files(struct tracecmd_output *handle)\n{\n\ttsize_t size, check_size, endian8;\n\tstruct stat st;\n\ttsize_t offset;\n\tchar *path;\n\tint fd = -1;\n\tint ret;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_HEADERS)) {\n\t\ttracecmd_warning(\"Cannot read header files, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\tpath = get_tracing_file(handle, \"events/header_page\");\n\tif (!path)\n\t\treturn -1;\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_HEADER_INFO,\n\t\t\t\t\t       \"headers\", true);\n\tif (offset == (off_t)-1) {\n\t\tput_tracing_file(path);\n\t\treturn -1;\n\t}\n\n\ttcmd_out_compression_start(handle);\n\tret = stat(path, &st);\n\tif (ret < 0) {\n\t\t/* old style did not show this info, just add zero */\n\t\tput_tracing_file(path);\n\t\tif (tcmd_do_write_check(handle, \"header_page\", 12))\n\t\t\tgoto out_close;\n\t\tsize = 0;\n\t\tif (tcmd_do_write_check(handle, &size, 8))\n\t\t\tgoto out_close;\n\t\tif (tcmd_do_write_check(handle, \"header_event\", 13))\n\t\t\tgoto out_close;\n\t\tif (tcmd_do_write_check(handle, &size, 8))\n\t\t\tgoto out_close;\n\t\tif (tcmd_out_compression_end(handle))\n\t\t\tgoto out_close;\n\t\tif (tcmd_out_update_section_header(handle, offset))\n\t\t\tgoto out_close;\n\t\treturn 0;\n\t}\n\n\tfd = open(path, O_RDONLY);\n\tif (fd < 0) {\n\t\ttracecmd_warning(\"can't read '%s'\", path);\n\t\tgoto out_free;\n\t}\n\n\t/* unfortunately, you can not stat debugfs files for size */\n\tsize = get_size_fd(fd);\n\n\tif (tcmd_do_write_check(handle, \"header_page\", 12))\n\t\tgoto out_free;\n\tendian8 = convert_endian_8(handle, size);\n\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\tgoto out_free;\n\tcheck_size = copy_file_fd(handle, fd, 0);\n\tif (size != check_size) {\n\t\ttracecmd_warning(\"wrong size for '%s' size=%lld read=%lld\", path, size, check_size);\n\t\terrno = EINVAL;\n\t\tgoto out_free;\n\t}\n\tput_tracing_file(path);\n\n\tpath = get_tracing_file(handle, \"events/header_event\");\n\tif (!path)\n\t\tgoto out_close;\n\n\tclose(fd);\n\tfd = open(path, O_RDONLY);\n\tif (fd < 0) {\n\t\ttracecmd_warning(\"can't read '%s'\", path);\n\t\tgoto out_free;\n\t}\n\n\tsize = get_size_fd(fd);\n\n\tif (tcmd_do_write_check(handle, \"header_event\", 13))\n\t\tgoto out_free;\n\tendian8 = convert_endian_8(handle, size);\n\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\tgoto out_free;\n\tcheck_size = copy_file_fd(handle, fd, 0);\n\tclose(fd);\n\tif (size != check_size) {\n\t\ttracecmd_warning(\"wrong size for '%s'\", path);\n\t\tgoto out_free;\n\t}\n\tput_tracing_file(path);\n\tif (tcmd_out_compression_end(handle))\n\t\tgoto out_close;\n\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\tgoto out_close;\n\thandle->file_state = TRACECMD_FILE_HEADERS;\n\n\treturn 0;\n\n out_free:\n\tput_tracing_file(path);\n out_close:\n\ttcmd_out_compression_reset(handle);\n\tif (fd >= 0)\n\t\tclose(fd);\n\treturn -1;\n}\n\nstatic int copy_event_system(struct tracecmd_output *handle,\n\t\t\t     struct list_event_system *slist)\n{\n\tstruct list_event *elist;\n\tunsigned long long size, check_size, endian8;\n\tstruct stat st;\n\tchar *format;\n\tint endian4;\n\tint count = 0;\n\tint ret;\n\n\tfor (elist = slist->events; elist; elist = elist->next)\n\t\tcount++;\n\n\tendian4 = convert_endian_4(handle, count);\n\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\treturn -1;\n\n\tfor (elist = slist->events; elist; elist = elist->next) {\n\t\tformat = elist->file;\n\t\tret = stat(format, &st);\n\n\t\tif (ret >= 0) {\n\t\t\t/* unfortunately, you can not stat debugfs files for size */\n\t\t\tsize = get_size(format);\n\t\t\tendian8 = convert_endian_8(handle, size);\n\t\t\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\t\t\treturn -1;\n\t\t\tcheck_size = copy_file(handle, format);\n\t\t\tif (size != check_size) {\n\t\t\t\ttracecmd_warning(\"error in size of file '%s'\", format);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nstatic void add_list_event_system(struct list_event_system **systems,\n\t\t\t\t  const char *system,\n\t\t\t\t  const char *event,\n\t\t\t\t  const char *path)\n{\n\tstruct list_event_system *slist;\n\tstruct list_event *elist;\n\n\tfor (slist = *systems; slist; slist = slist->next)\n\t\tif (strcmp(slist->name, system) == 0)\n\t\t\tbreak;\n\n\tif (!slist) {\n\t\tslist = malloc(sizeof(*slist));\n\t\tif (!slist)\n\t\t\tgoto err_mem;\n\t\tslist->name = strdup(system);\n\t\tif (!slist->name) {\n\t\t\tfree(slist);\n\t\t\tgoto err_mem;\n\t\t}\n\t\tslist->next = *systems;\n\t\tslist->events = NULL;\n\t\t*systems = slist;\n\t}\n\n\tfor (elist = slist->events; elist; elist = elist->next)\n\t\tif (strcmp(elist->name, event) == 0)\n\t\t\tbreak;\n\n\tif (!elist) {\n\t\telist = malloc(sizeof(*elist));\n\t\tif (!elist)\n\t\t\tgoto err_mem;\n\t\telist->name = strdup(event);\n\t\telist->file = strdup(path);\n\t\tif (!elist->name || !elist->file) {\n\t\t\tfree(elist->name);\n\t\t\tfree(elist->file);\n\t\t\tfree(elist);\n\t\t\tgoto err_mem;\n\t\t}\n\t\telist->next = slist->events;\n\t\tslist->events = elist;\n\t}\n\treturn;\n err_mem:\n\t tracecmd_warning(\"Insufficient memory\");\n}\n\nstatic void free_list_events(struct list_event_system *list)\n{\n\tstruct list_event_system *slist;\n\tstruct list_event *elist;\n\n\twhile (list) {\n\t\tslist = list;\n\t\tlist = list->next;\n\t\twhile (slist->events) {\n\t\t\telist = slist->events;\n\t\t\tslist->events = elist->next;\n\t\t\tfree(elist->name);\n\t\t\tfree(elist->file);\n\t\t\tfree(elist);\n\t\t}\n\t\tfree(slist->name);\n\t\tfree(slist);\n\t}\n}\n\nstatic void glob_events(struct tracecmd_output *handle,\n\t\t\tstruct list_event_system **systems,\n\t\t\tconst char *str)\n{\n\tglob_t globbuf;\n\tchar *events_path;\n\tchar *system;\n\tchar *event;\n\tchar *path;\n\tchar *file;\n\tchar *ptr;\n\tint do_ftrace = 0;\n\tint events_len;\n\tint ret;\n\tint i;\n\n\tif (strncmp(str, \"ftrace/\", 7) == 0)\n\t\tdo_ftrace = 1;\n\n\tevents_path = get_tracing_file(handle, \"events\");\n\tevents_len = strlen(events_path);\n\n\tpath = malloc(events_len + strlen(str) +\n\t\t      strlen(\"/format\") + 2);\n\tif (!path) {\n\t\tput_tracing_file(events_path);\n\t\treturn;\n\t}\n\tpath[0] = '\\0';\n\tstrcat(path, events_path);\n\tstrcat(path, \"/\");\n\tstrcat(path, str);\n\tstrcat(path, \"/format\");\n\tput_tracing_file(events_path);\n\n\tglobbuf.gl_offs = 0;\n\tret = glob(path, 0, NULL, &globbuf);\n\tfree(path);\n\t/* no request flags, so only GLOB_NOSPACE and GLOB_NOMATCH */\n\tif (ret != 0)\n\t\treturn;\n\n\tfor (i = 0; i < globbuf.gl_pathc; i++) {\n\t\tfile = globbuf.gl_pathv[i];\n\t\tsystem = strdup(file + events_len + 1);\n\t\tsystem = strtok_r(system, \"/\", &ptr);\n\t\tif (!ptr) {\n\t\t\t/* ?? should we warn? */\n\t\t\tfree(system);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!do_ftrace && strcmp(system, \"ftrace\") == 0) {\n\t\t\tfree(system);\n\t\t\tcontinue;\n\t\t}\n\n\t\tevent = strtok_r(NULL, \"/\", &ptr);\n\t\tif (!ptr) {\n\t\t\t/* ?? should we warn? */\n\t\t\tfree(system);\n\t\t\tcontinue;\n\t\t}\n\n\t\tadd_list_event_system(systems, system, event, file);\n\t\tfree(system);\n\t}\n\tglobfree(&globbuf);\n}\n\nstatic void\ncreate_event_list_item(struct tracecmd_output *handle,\n\t\t       struct list_event_system **systems,\n\t\t       struct tracecmd_event_list *list)\n{\n\tchar *ptr;\n\tchar *str;\n\n\tstr = strdup(list->glob);\n\tif (!str)\n\t\tgoto err_mem;\n\n\t/* system and event names are separated by a ':' */\n\tptr = strchr(str, ':');\n\tif (ptr)\n\t\t*ptr = '/';\n\telse\n\t\t/* system and event may also be separated by a '/' */\n\t\tptr = strchr(str, '/');\n\n\tif (ptr) {\n\t\tglob_events(handle, systems, str);\n\t\tfree(str);\n\t\treturn;\n\t}\n\n\tptr = str;\n\tstr = malloc(strlen(ptr) + 3);\n\tif (!str)\n\t\tgoto err_mem;\n\tstr[0] = '\\0';\n\tstrcat(str, ptr);\n\tstrcat(str, \"/*\");\n\tglob_events(handle, systems, str);\n\n\tstr[0] = '\\0';\n\tstrcat(str, \"*/\");\n\tstrcat(str, ptr);\n\tglob_events(handle, systems, str);\n\n\tfree(ptr);\n\tfree(str);\n\treturn;\n err_mem:\n\t tracecmd_warning(\"Insufficient memory\");\n}\n\nstatic int read_ftrace_files(struct tracecmd_output *handle)\n{\n\tstruct list_event_system *systems = NULL;\n\tstruct tracecmd_event_list list = { .glob = \"ftrace/*\" };\n\ttsize_t offset;\n\tint ret;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_FTRACE_EVENTS)) {\n\t\ttracecmd_warning(\"Cannot read ftrace files, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_FTRACE_EVENTS,\n\t\t\t\t\t       \"ftrace events\", true);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\tcreate_event_list_item(handle, &systems, &list);\n\ttcmd_out_compression_start(handle);\n\n\tret = copy_event_system(handle, systems);\n\tif (!ret)\n\t\tret = tcmd_out_compression_end(handle);\n\telse\n\t\ttcmd_out_compression_reset(handle);\n\n\tfree_list_events(systems);\n\tif (ret)\n\t\treturn ret;\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\treturn -1;\n\n\thandle->file_state = TRACECMD_FILE_FTRACE_EVENTS;\n\n\treturn ret;\n}\n\nstatic struct list_event_system *\ncreate_event_list(struct tracecmd_output *handle,\n\t\t  struct tracecmd_event_list *event_list)\n{\n\tstruct list_event_system *systems = NULL;\n\tstruct tracecmd_event_list *list;\n\n\tfor (list = event_list; list; list = list->next)\n\t\tcreate_event_list_item(handle, &systems, list);\n\n\treturn systems;\n}\n\nstatic int read_event_files(struct tracecmd_output *handle,\n\t\t\t    struct tracecmd_event_list *event_list)\n{\n\tstruct list_event_system *systems;\n\tstruct list_event_system *slist;\n\tstruct tracecmd_event_list *list;\n\tstruct tracecmd_event_list all_events = { .glob = \"*/*\" };\n\tint count = 0;\n\ttsize_t offset;\n\tint endian4;\n\tint ret;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_ALL_EVENTS)) {\n\t\ttracecmd_warning(\"Cannot read event files, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_EVENT_FORMATS,\n\t\t\t\t\t       \"events format\", true);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\t/*\n\t * If any of the list is the special keyword \"all\" then\n\t * just do all files.\n\t */\n\tfor (list = event_list; list; list = list->next) {\n\t\tif (strcmp(list->glob, \"all\") == 0)\n\t\t\tbreak;\n\t}\n\t/* all events are listed, use a global glob */\n\tif (!event_list || list)\n\t\tevent_list = &all_events;\n\n\tsystems = create_event_list(handle, event_list);\n\n\tfor (slist = systems; slist; slist = slist->next)\n\t\tcount++;\n\ttcmd_out_compression_start(handle);\n\tret = -1;\n\tendian4 = convert_endian_4(handle, count);\n\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\tgoto out_free;\n\n\tret = 0;\n\tfor (slist = systems; !ret && slist; slist = slist->next) {\n\t\tif (tcmd_do_write_check(handle, slist->name,\n\t\t\t\t   strlen(slist->name) + 1)) {\n\t\t\tret = -1;\n\t\t\tcontinue;\n\t\t}\n\t\tret = copy_event_system(handle, slist);\n\t}\n\tif (ret)\n\t\tgoto out_free;\n\n\tret = tcmd_out_compression_end(handle);\n\tif (ret)\n\t\tgoto out_free;\n\tret = tcmd_out_update_section_header(handle, offset);\n\n out_free:\n\tif (!ret)\n\t\thandle->file_state = TRACECMD_FILE_ALL_EVENTS;\n\telse\n\t\ttcmd_out_compression_reset(handle);\n\n\tfree_list_events(systems);\n\n\treturn ret;\n}\n\n#define KPTR_UNINITIALIZED 'X'\n\nstatic void set_proc_kptr_restrict(int reset)\n{\n\tchar *path = \"/proc/sys/kernel/kptr_restrict\";\n\tstatic char saved = KPTR_UNINITIALIZED;\n\tint fd, ret = -1;\n\tstruct stat st;\n\tchar buf;\n\n\tif ((reset && saved == KPTR_UNINITIALIZED) ||\n\t    (stat(path, &st) < 0))\n\t\treturn;\n\n\tfd = open(path, O_RDONLY);\n\tif (fd < 0)\n\t\tgoto err;\n\n\tif (reset) {\n\t\tbuf = saved;\n\t} else {\n\t\tif (read(fd, &buf, 1) < 0)\n\t\t\tgoto err;\n\t\tsaved = buf;\n\t\tbuf = '0';\n\t}\n\tclose(fd);\n\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0)\n\t\tgoto err;\n\tif (write(fd, &buf, 1) > 0)\n\t\tret = 0;\nerr:\n\tif (fd >= 0)\n\t\tclose(fd);\n\tif (ret)\n\t\ttracecmd_warning(\"can't set kptr_restrict\");\n}\n\nstatic int read_proc_kallsyms(struct tracecmd_output *handle)\n{\n\tunsigned int size, check_size, endian4;\n\tconst char *path = \"/proc/kallsyms\";\n\toff_t size_offset;\n\ttsize_t offset;\n\tstruct stat st;\n\tint ret;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_KALLSYMS)) {\n\t\ttracecmd_warning(\"Cannot read kallsyms, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\tif (handle->kallsyms)\n\t\tpath = handle->kallsyms;\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_KALLSYMS,\n\t\t\t\t\t       \"kallsyms\", true);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\ttcmd_out_compression_start(handle);\n\tret = stat(path, &st);\n\tif (ret < 0) {\n\t\t/* not found */\n\t\tsize = 0;\n\t\tendian4 = convert_endian_4(handle, size);\n\t\tret = tcmd_do_write_check(handle, &endian4, 4);\n\t\tgoto out;\n\t}\n\tsize = get_size(path);\n\tendian4 = convert_endian_4(handle, size);\n\tsize_offset = do_lseek(handle, 0, SEEK_CUR);\n\tret = tcmd_do_write_check(handle, &endian4, 4);\n\tif (ret)\n\t\tgoto out;\n\n\tset_proc_kptr_restrict(0);\n\tcheck_size = copy_file(handle, path);\n\tif (size != check_size &&\n\t    update_endian_4(handle, size_offset, check_size)) {\n\t\terrno = EINVAL;\n\t\ttracecmd_warning(\"error in size of file '%s'\", path);\n\t\tset_proc_kptr_restrict(1);\n\t\tret = -1;\n\t\tgoto out;\n\t}\n\tset_proc_kptr_restrict(1);\n\n\tret = tcmd_out_compression_end(handle);\n\tif (ret)\n\t\tgoto out;\n\n\tret = tcmd_out_update_section_header(handle, offset);\nout:\n\tif (!ret)\n\t\thandle->file_state = TRACECMD_FILE_KALLSYMS;\n\telse\n\t\ttcmd_out_compression_reset(handle);\n\treturn ret;\n}\n\nstatic int read_ftrace_printk(struct tracecmd_output *handle)\n{\n\tunsigned int size, check_size, endian4;\n\ttsize_t offset;\n\tstruct stat st;\n\tchar *path;\n\tint ret;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_PRINTK)) {\n\t\ttracecmd_warning(\"Cannot read printk, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\tpath = get_tracing_file(handle, \"printk_formats\");\n\tif (!path)\n\t\treturn -1;\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_PRINTK, \"printk\", true);\n\tif (offset == (off_t)-1) {\n\t\tput_tracing_file(path);\n\t\treturn -1;\n\t}\n\n\ttcmd_out_compression_start(handle);\n\tret = stat(path, &st);\n\tif (ret < 0) {\n\t\t/* not found */\n\t\tsize = 0;\n\t\tendian4 = convert_endian_4(handle, size);\n\t\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\t\tgoto fail;\n\t\tgoto out;\n\t}\n\tsize = get_size(path);\n\tendian4 = convert_endian_4(handle, size);\n\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\tgoto fail;\n\tcheck_size = copy_file(handle, path);\n\tif (size != check_size) {\n\t\terrno = EINVAL;\n\t\ttracecmd_warning(\"error in size of file '%s'\", path);\n\t\tgoto fail;\n\t}\n\n out:\n\tput_tracing_file(path);\n\tif (tcmd_out_compression_end(handle))\n\t\treturn -1;\n\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\treturn -1;\n\thandle->file_state = TRACECMD_FILE_PRINTK;\n\treturn 0;\n fail:\n\tput_tracing_file(path);\n\ttcmd_out_compression_reset(handle);\n\treturn -1;\n}\n\nstatic int save_tracing_file_data(struct tracecmd_output *handle,\n\t\t\t\t  const char *filename)\n{\n\tunsigned long long endian8;\n\tchar *file = NULL;\n\tstruct stat st;\n\toff_t check_size;\n\toff_t size;\n\tint ret = -1;\n\n\tfile = get_tracing_file(handle, filename);\n\tif (!file)\n\t\treturn -1;\n\n\tret = stat(file, &st);\n\tif (ret >= 0) {\n\t\tsize = get_size(file);\n\t\tendian8 = convert_endian_8(handle, size);\n\t\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\t\tgoto out_free;\n\t\tcheck_size = copy_file(handle, file);\n\t\tif (size != check_size) {\n\t\t\terrno = EINVAL;\n\t\t\ttracecmd_warning(\"error in size of file '%s'\", file);\n\t\t\tgoto out_free;\n\t\t}\n\t} else {\n\t\tsize = 0;\n\t\tendian8 = convert_endian_8(handle, size);\n\t\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\t\tgoto out_free;\n\t}\n\tret = 0;\n\nout_free:\n\tput_tracing_file(file);\n\treturn ret;\n}\n\nstatic int write_compression_header(struct tracecmd_output *handle)\n{\n\tconst char *name = NULL;\n\tconst char *ver = NULL;\n\tint ret;\n\n\tret = tracecmd_compress_proto_get_name(handle->compress, &name, &ver);\n\tif (ret < 0 || !name || !ver) {\n\t\tname = \"none\";\n\t\tver = \"\";\n\t}\n\n\tif (tcmd_do_write_check(handle, name, strlen(name) + 1))\n\t\treturn -1;\n\n\tif (tcmd_do_write_check(handle, ver, strlen(ver) + 1))\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic int get_trace_page_size(struct tracecmd_output *handle, const char *name)\n{\n\tstruct tracefs_instance *instance;\n\tstruct tep_handle *tep = NULL;\n\tint psize, size;\n\tchar *buff = NULL;\n\n\t/* In case of an error, return user space page size */\n\tpsize = getpagesize();\n\n\tinstance = tracefs_instance_alloc(find_tracing_dir(handle), name);\n\tif (!instance)\n\t\tgoto out;\n\n\tbuff = tracefs_instance_file_read(instance, \"events/header_page\", &size);\n\tif (!buff)\n\t\tgoto out;\n\n\ttep = tep_alloc();\n\tif (!tep)\n\t\tgoto out;\n\n\tif (tep_parse_header_page(tep, buff, size, sizeof(long long)))\n\t\tgoto out;\n\n\tpsize = tep_get_sub_buffer_size(tep);\n\nout:\n\ttracefs_instance_free(instance);\n\ttep_free(tep);\n\tfree(buff);\n\n\treturn psize;\n}\n\n/**\n * tracecmd_output_create_fd - allocate new output handle to a trace file\n * @fd: File descriptor for the handle to write to.\n *\n * Allocate a tracecmd_output descriptor and perform minimal initialization.\n * @fd will be set as the file descriptor for the handle. Nothing is\n * written in the file yet, and if @fd is -1, then all writes will be ignored.\n *\n * Returns a pointer to a newly allocated file descriptor for the use of creating\n * a tracecmd data file. In case of an error, NULL is returned. The returned\n * handle must be freed with tracecmd_output_close() or tracecmd_output_free()\n */\nstruct tracecmd_output *tracecmd_output_create_fd(int fd)\n{\n\tstruct tracecmd_output *handle;\n\n\thandle = calloc(1, sizeof(*handle));\n\tif (!handle)\n\t\treturn NULL;\n\n\thandle->fd = fd;\n\n\thandle->file_version = FILE_VERSION_DEFAULT;\n\n\thandle->page_size = get_trace_page_size(handle, NULL);\n\thandle->big_endian = tracecmd_host_bigendian();\n\n\tlist_head_init(&handle->options);\n\tlist_head_init(&handle->buffers);\n\n\thandle->file_state = TRACECMD_FILE_ALLOCATED;\n\n\treturn handle;\n}\n\n/**\n * tracecmd_output_set_msg - associated an output file handle with network message handle\n * @handle: output handle to a trace file.\n * @msg_handle: network handle, allocated by tracecmd_msg_handle_alloc()\n *\n * Associate an output file handle (@handle) to a network stream (@msg_handle).\n * All subsequent calls to @handle will send data over the network using @msg_handle\n * instead of writing to a file.\n *\n * This must be called after the handle file version is set and before calling\n * tracecmd_output_write_headers().\n *\n * Returns 0 on success, or -1 if the output file handle is not allocated or not\n * in the expected state.\n */\nint tracecmd_output_set_msg(struct tracecmd_output *handle, struct tracecmd_msg_handle *msg_handle)\n{\n\tif (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)\n\t\treturn -1;\n\n\thandle->msg_handle = msg_handle;\n\t/* Force messages to be cached in a temp file before sending through the socket */\n\tif (handle->msg_handle && HAS_SECTIONS(handle))\n\t\ttracecmd_msg_handle_cache(handle->msg_handle);\n\n\treturn 0;\n}\n\n/**\n * tracecmd_output_set_trace_dir - Set a custom tracing dir, instead of system default\n * @handle: output handle to a trace file.\n * @tracing_dir: full path to a directory with tracing files\n *\n * Associate the output file handle (@handle) with a custom tracing directory\n * (@tracing_dir), to be used when creating the trace file instead of using the\n * system default tracig directory.\n *\n * Must be called before tracecmd_output_write_headers().\n *\n * Returns 0 on success, or -1 if the output file handle is not allocated or not\n * in the expected state.\n */\nint tracecmd_output_set_trace_dir(struct tracecmd_output *handle, const char *tracing_dir)\n{\n\tif (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)\n\t\treturn -1;\n\n\tfree(handle->tracing_dir);\n\tif (tracing_dir) {\n\t\thandle->tracing_dir = strdup(tracing_dir);\n\t\tif (!handle->tracing_dir)\n\t\t\treturn -1;\n\t} else\n\t\thandle->tracing_dir = NULL;\n\n\treturn 0;\n}\n\n/**\n * tracecmd_output_set_kallsyms - Set a custom kernel symbols file\n * @handle: output handle to a trace file.\n * @tracing_dir: full path to a file with kernel symbols\n *\n * Have the output file handle (@handle) use a custom kernel symbols file instead\n * of the default /proc/kallsyms.\n *\n * Must be called before tracecmd_output_write_headers().\n *\n * Returns 0 on success, or -1 if the output file handle is not allocated or\n * not in the expected state.\n */\nint tracecmd_output_set_kallsyms(struct tracecmd_output *handle, const char *kallsyms)\n{\n\tif (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)\n\t\treturn -1;\n\n\tfree(handle->kallsyms);\n\tif (kallsyms) {\n\t\thandle->kallsyms = strdup(kallsyms);\n\t\tif (!handle->kallsyms)\n\t\t\treturn -1;\n\t} else\n\t\thandle->kallsyms = NULL;\n\n\treturn 0;\n}\n\n/**\n * tracecmd_output_set_from_input - Inherit parameters from an existing trace file\n * @handle: output handle to a trace file.\n * @ihandle: input handle to an existing trace file.\n *\n * Have the output file handle (@handle) inherit the properties of a given\n * input file handle (@ihandle).\n *\n * The parameters that are copied are:\n *  - tep handle\n *  - page size\n *  - file endian\n *  - file version\n *  - file compression protocol\n *\n * Must be called before tracecmd_output_write_headers().\n *\n * Returns 0 on success, or -1 if the output file handle is not allocated or\n * not in expected state.\n */\nint tracecmd_output_set_from_input(struct tracecmd_output *handle, struct tracecmd_input *ihandle)\n{\n\tconst char *cname = NULL;\n\tconst char *cver = NULL;\n\n\tif (!handle || !ihandle || handle->file_state != TRACECMD_FILE_ALLOCATED)\n\t\treturn -1;\n\n\t/* get endian, page size, file version and compression */\n\t/* Use the pevent of the ihandle for later writes */\n\thandle->pevent = tracecmd_get_tep(ihandle);\n\ttep_ref(handle->pevent);\n\thandle->page_size = tracecmd_page_size(ihandle);\n\thandle->file_version = tracecmd_get_in_file_version(ihandle);\n\thandle->big_endian = tep_is_file_bigendian(handle->pevent);\n\n\tif (!tracecmd_get_file_compress_proto(ihandle, &cname, &cver)) {\n\t\thandle->compress = tracecmd_compress_alloc(cname, cver, handle->fd,\n\t\t\t\t\t\t\t    handle->pevent, handle->msg_handle);\n\t\tif (!handle->compress)\n\t\t\treturn -1;\n\n\t\tif (handle->file_version < FILE_VERSION_COMPRESSION)\n\t\t\thandle->file_version = FILE_VERSION_COMPRESSION;\n\t}\n\n\treturn 0;\n}\n\n/**\n * tracecmd_output_set_version - Set file version of the output handle\n * @handle: output handle to a trace file.\n * @file_version: desired file version\n *\n * This API must be called before tracecmd_output_write_headers().\n *\n * Returns 0 on success, or -1 if the output file handle is not allocated or not in expected state.\n */\nint tracecmd_output_set_version(struct tracecmd_output *handle, int file_version)\n{\n\tif (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)\n\t\treturn -1;\n\tif (file_version < FILE_VERSION_MIN || file_version > FILE_VERSION_MAX)\n\t\treturn -1;\n\thandle->file_version = file_version;\n\tif (handle->file_version < FILE_VERSION_COMPRESSION)\n\t\thandle->compress = NULL;\n\treturn 0;\n}\n\n/**\n * tracecmd_output_set_compression - Set file compression algorithm of the output handle\n * @handle: output handle to a trace file.\n * @compression: name of the desired compression algorithm. Can be one of:\n *\t\t - \"none\" - do not use compression\n *\t\t - \"all\" - use the best available compression algorithm\n *\t\t - or specific name of the desired compression algorithm\n *\n * This API must be called before tracecmd_output_write_headers().\n *\n * Returns 0 on success, or -1 in case of an error:\n *   - the output file handle is not allocated or not in expected state.\n *   - the specified compression algorithm is not available\n */\nint tracecmd_output_set_compression(struct tracecmd_output *handle, const char *compression)\n{\n\tif (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)\n\t\treturn -1;\n\n\thandle->compress = NULL;\n\tif (compression && strcmp(compression, \"none\")) {\n\t\tif (!strcmp(compression, \"any\")) {\n\t\t\thandle->compress = tracecmd_compress_alloc(NULL, NULL, handle->fd,\n\t\t\t\t\t\t\t\t    handle->pevent,\n\t\t\t\t\t\t\t\t    handle->msg_handle);\n\t\t\tif (!handle->compress)\n\t\t\t\ttracecmd_warning(\"No compression algorithms are supported\");\n\t\t} else {\n\t\t\thandle->compress = tracecmd_compress_alloc(compression, NULL, handle->fd,\n\t\t\t\t\t\t\t\t    handle->pevent,\n\t\t\t\t\t\t\t\t    handle->msg_handle);\n\t\t\tif (!handle->compress) {\n\t\t\t\ttracecmd_warning(\"Compression algorithm %s is not supported\",\n\t\t\t\t\t\t  compression);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t}\n\tif (handle->compress && handle->file_version < FILE_VERSION_COMPRESSION) {\n\t\thandle->file_version = FILE_VERSION_COMPRESSION;\n\t\tif (handle->msg_handle)\n\t\t\ttracecmd_msg_handle_cache(handle->msg_handle);\n\t}\n\n\treturn 0;\n}\n\n/**\n * output_write_init - Write the initial data into the trace file\n * @handle: output handle to a trace file.\n *\n * Must be called after tracecmd_output_set_*() functions and before writing\n * anything else.\n *\n * The initial information to be written into the file:\n *  - initial file magic bytes\n *  - file version\n *  - data endian\n *  - long size\n *  - page size\n *  - compression header\n *\n * Returns 0 on success, or -1 if the output file handle is not allocated or\n * not in the expected state.\n */\nstatic int output_write_init(struct tracecmd_output *handle)\n{\n\tunsigned long long offset;\n\tchar buf[BUFSIZ];\n\tint endian4;\n\n\tif (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)\n\t\treturn -1;\n\n\tbuf[0] = 23;\n\tbuf[1] = 8;\n\tbuf[2] = 68;\n\tmemcpy(buf + 3, \"tracing\", 7);\n\n\tif (tcmd_do_write_check(handle, buf, 10))\n\t\treturn -1;\n\n\tsprintf(buf, \"%lu\", handle->file_version);\n\tif (tcmd_do_write_check(handle, buf, strlen(buf) + 1))\n\t\treturn -1;\n\n\tif (handle->big_endian)\n\t\tbuf[0] = 1;\n\telse\n\t\tbuf[0] = 0;\n\tif (tcmd_do_write_check(handle, buf, 1))\n\t\treturn -1;\n\n\t/* save size of long (this may not be what the kernel is) */\n\tbuf[0] = sizeof(long);\n\tif (tcmd_do_write_check(handle, buf, 1))\n\t\treturn -1;\n\n\tendian4 = convert_endian_4(handle, handle->page_size);\n\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\treturn -1;\n\n\tif (handle->file_version >= FILE_VERSION_COMPRESSION) {\n\t\tif (write_compression_header(handle))\n\t\t\treturn -1;\n\t}\n\n\tif (HAS_SECTIONS(handle)) {\n\t\t/* Write 0 as options offset and save its location */\n\t\toffset = 0;\n\t\thandle->options_start = do_lseek(handle, 0, SEEK_CUR);\n\t\tif (tcmd_do_write_check(handle, &offset, 8))\n\t\t\treturn -1;\n\t}\n\n\thandle->file_state = TRACECMD_FILE_INIT;\n\treturn 0;\n}\n\n/**\n * tracecmd_output_write_headers - Write the trace file headers\n * @handle: output handle to a trace file.\n * @list: desired events that will be included in the trace file.\n *\t  It can be NULL for all available events\n *\n * These headers are written in the file:\n *  - header files from the tracing directory\n *  - ftrace events from the tracing directory\n *  - event file from the tracing directory - all or only the one from @list\n *  - kernel symbols from the tracing directory\n *  - kernel printk strings from the tracing directory\n *\n * Returns 0 on success, or -1 in case of an error.\n */\nint tracecmd_output_write_headers(struct tracecmd_output *handle,\n\t\t\t\t  struct tracecmd_event_list *list)\n{\n\tif (!handle || handle->file_state < TRACECMD_FILE_ALLOCATED)\n\t\treturn -1;\n\n\t/* Write init data, if not written yet */\n\tif (handle->file_state < TRACECMD_FILE_INIT && output_write_init(handle))\n\t\treturn -1;\n\tif (read_header_files(handle))\n\t\treturn -1;\n\tif (read_ftrace_files(handle))\n\t\treturn -1;\n\tif (read_event_files(handle, list))\n\t\treturn -1;\n\tif (read_proc_kallsyms(handle))\n\t\treturn -1;\n\tif (read_ftrace_printk(handle))\n\t\treturn -1;\n\treturn 0;\n}\n\n/**\n * tracecmd_add_option_v - add options to the file\n * @handle: the output file handle name\n * @id: the id of the option\n * @size: the size of the option data\n * @data: the data to write to the file\n * @vector: array of vectors, pointing to the data to write in the file\n * @count: number of items in the vector array\n *\n *\n * Returns handle to update option if needed.\n *  Just the content can be updated, with smaller or equal to\n *  content than the specified size.\n */\nstruct tracecmd_option *\ntracecmd_add_option_v(struct tracecmd_output *handle,\n\t\t      unsigned short id, const struct iovec *vector, int count)\n\n{\n\tstruct tracecmd_option *option;\n\tchar *data = NULL;\n\tint i, size = 0;\n\n\t/*\n\t * We can only add options before tracing data were written.\n\t * This may change in the future.\n\t */\n\tif (!HAS_SECTIONS(handle) && handle->file_state > TRACECMD_FILE_OPTIONS)\n\t\treturn NULL;\n\n\tfor (i = 0; i < count; i++)\n\t\tsize += vector[i].iov_len;\n\t/* Some IDs (like TRACECMD_OPTION_TRACECLOCK) pass vector with 0 / NULL data */\n\tif (size) {\n\t\tdata = malloc(size);\n\t\tif (!data) {\n\t\t\ttracecmd_warning(\"Insufficient memory\");\n\t\t\treturn NULL;\n\t\t}\n\t}\n\toption = calloc(1, sizeof(*option));\n\tif (!option) {\n\t\ttracecmd_warning(\"Could not allocate space for option\");\n\t\tfree(data);\n\t\treturn NULL;\n\t}\n\n\thandle->nr_options++;\n\toption->data = data;\n\tfor (i = 0; i < count; i++) {\n\t\tif (vector[i].iov_base && vector[i].iov_len) {\n\t\t\tmemcpy(data, vector[i].iov_base, vector[i].iov_len);\n\t\t\tdata += vector[i].iov_len;\n\t\t}\n\t}\n\n\toption->size = size;\n\toption->id = id;\n\n\tlist_add_tail(&option->list, &handle->options);\n\n\treturn option;\n}\n\n/**\n * tracecmd_add_option - add options to the file\n * @handle: the output file handle name\n * @id: the id of the option\n * @size: the size of the option data\n * @data: the data to write to the file\n *\n * Returns handle to update option if needed\n *  Just the content can be updated, with smaller or equal to\n *  content than the specified size\n */\nstruct tracecmd_option *\ntracecmd_add_option(struct tracecmd_output *handle,\n\t\t    unsigned short id, int size, const void *data)\n{\n\tstruct iovec vect;\n\n\tvect.iov_base = (void *) data;\n\tvect.iov_len = size;\n\treturn tracecmd_add_option_v(handle, id, &vect, 1);\n}\n\nint tracecmd_write_cpus(struct tracecmd_output *handle, int cpus)\n{\n\tint ret;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_CPU_COUNT)) {\n\t\ttracecmd_warning(\"Cannot write CPU count into the file, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\tif (!HAS_SECTIONS(handle)) {\n\t\tcpus = convert_endian_4(handle, cpus);\n\t\tret = tcmd_do_write_check(handle, &cpus, 4);\n\t\tif (ret < 0)\n\t\t\treturn ret;\n\t} else {\n\t\ttracecmd_add_option(handle, TRACECMD_OPTION_CPUCOUNT, sizeof(int), &cpus);\n\t}\n\n\thandle->file_state = TRACECMD_FILE_CPU_COUNT;\n\treturn 0;\n}\n\nstatic int write_options_v6(struct tracecmd_output *handle)\n{\n\tstruct tracecmd_option *options;\n\tunsigned short option;\n\tunsigned short endian2;\n\tunsigned int endian4;\n\n\t/* If already written, ignore */\n\tif (handle->file_state == TRACECMD_FILE_OPTIONS)\n\t\treturn 0;\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_OPTIONS)) {\n\t\ttracecmd_warning(\"Cannot write options into the file, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\tif (tcmd_do_write_check(handle, \"options  \", 10))\n\t\treturn -1;\n\thandle->options_start = do_lseek(handle, 0, SEEK_CUR);\n\tlist_for_each_entry(options, &handle->options, list) {\n\t\tendian2 = convert_endian_2(handle, options->id);\n\t\tif (tcmd_do_write_check(handle, &endian2, 2))\n\t\t\treturn -1;\n\n\t\tendian4 = convert_endian_4(handle, options->size);\n\t\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\t\treturn -1;\n\n\t\t/* Save the data location in case it needs to be updated */\n\t\toptions->offset = do_lseek(handle, 0, SEEK_CUR);\n\n\t\tif (tcmd_do_write_check(handle, options->data,\n\t\t\t\t   options->size))\n\t\t\treturn -1;\n\t}\n\n\toption = TRACECMD_OPTION_DONE;\n\n\tif (tcmd_do_write_check(handle, &option, 2))\n\t\treturn -1;\n\n\thandle->file_state = TRACECMD_FILE_OPTIONS;\n\treturn 0;\n}\n\nstatic int update_options_start(struct tracecmd_output *handle, off_t offset)\n{\n\tif (do_lseek(handle, handle->options_start, SEEK_SET) == (off_t)-1)\n\t\treturn -1;\n\toffset = convert_endian_8(handle, offset);\n\tif (tcmd_do_write_check(handle, &offset, 8))\n\t\treturn -1;\n\treturn 0;\n}\n\n/**\n * tracecmd_pepare_options - perpare a previous options for the next\n * @handle: The handle to update the options for.\n * @offset: The offset to set the previous options to.\n * @whence: Where in the file to offset from.\n *\n * In a case of cached writes for network access, the options offset\n * cannot be written once it goes over the network. This is used\n * to update the next options to a known location.\n *\n * tracecmd_write_options() must be called when the offset is at the next\n * location, otherwise the data file will end up corrupted.\n *\n * Returns zero on success and -1 on error.\n */\nint tracecmd_prepare_options(struct tracecmd_output *handle, off_t offset, int whence)\n{\n\ttsize_t curr;\n\tint ret;\n\n\t/* No options to start with? */\n\tif (!handle->options_start)\n\t\treturn 0;\n\n\tcurr = do_lseek(handle, 0, SEEK_CUR);\n\n\tswitch (whence) {\n\tcase SEEK_SET:\n\t\t/* just use offset */\n\t\tbreak;\n\tcase SEEK_CUR:\n\t\toffset += curr;\n\t\tbreak;\n\tcase SEEK_END:\n\t\toffset = do_lseek(handle, offset, SEEK_END);\n\t\tif (offset == (off_t)-1)\n\t\t\treturn -1;\n\t\tbreak;\n\t}\n\tret = update_options_start(handle, offset);\n\tif (ret < 0)\n\t\treturn -1;\n\n\thandle->options_next = offset;\n\n\tcurr = do_lseek(handle, curr, SEEK_SET);\n\n\treturn curr == -1 ? -1 : 0;\n}\n\nstatic tsize_t write_options_start(struct tracecmd_output *handle)\n{\n\ttsize_t offset;\n\tint ret;\n\n\toffset = do_lseek(handle, 0, SEEK_CUR);\n\n\tif (handle->options_next) {\n\t\t/* options_start was already updated */\n\t\tif (handle->options_next != offset) {\n\t\t\ttracecmd_warning(\"Options offset (%lld) does not match expected (%lld)\",\n\t\t\t\t\t offset, handle->options_next);\n\t\t\treturn -1;\n\t\t}\n\t\thandle->options_next = 0;\n\t\t/* Will be updated at the end */\n\t\thandle->options_start = 0;\n\t}\n\n\t/* Append to the previous options section, if any */\n\tif (handle->options_start) {\n\t\tret = update_options_start(handle, offset);\n\t\tif (ret < 0)\n\t\t\treturn -1;\n\t\toffset = do_lseek(handle, offset, SEEK_SET);\n\t\tif (offset == (off_t)-1)\n\t\t\treturn -1;\n\t}\n\n\treturn tcmd_out_write_section_header(handle, TRACECMD_OPTION_DONE, \"options\", 0, false);\n}\n\nstatic tsize_t write_options_end(struct tracecmd_output *handle, tsize_t offset)\n{\n\tunsigned long long endian8;\n\tunsigned short endian2;\n\tunsigned int endian4;\n\n\tendian2 = convert_endian_2(handle, TRACECMD_OPTION_DONE);\n\tif (tcmd_do_write_check(handle, &endian2, 2))\n\t\treturn -1;\n\tendian4 = convert_endian_4(handle, 8);\n\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\treturn -1;\n\tendian8 = 0;\n\thandle->options_start = do_lseek(handle, 0, SEEK_CUR);\n\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\treturn -1;\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic int write_options(struct tracecmd_output *handle)\n{\n\tstruct tracecmd_option *options;\n\tunsigned short endian2;\n\tunsigned int endian4;\n\tbool new = false;\n\ttsize_t offset;\n\n\t/* Check if there are unsaved options */\n\tlist_for_each_entry(options, &handle->options, list) {\n\t\tif (!options->offset) {\n\t\t\tnew = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\t/*\n\t * Even if there are no new options, if options_next is set, it requires\n\t * adding a new empty options section as the previous one already\n\t * points to it.\n\t */\n\tif (!new && !handle->options_next)\n\t\treturn 0;\n\n\toffset = write_options_start(handle);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\tlist_for_each_entry(options, &handle->options, list) {\n\t\t/* Option is already saved, skip it */\n\t\tif (options->offset)\n\t\t\tcontinue;\n\t\tendian2 = convert_endian_2(handle, options->id);\n\t\tif (tcmd_do_write_check(handle, &endian2, 2))\n\t\t\treturn -1;\n\t\tendian4 = convert_endian_4(handle, options->size);\n\t\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\t\treturn -1;\n\t\t/* Save the data location */\n\t\toptions->offset = do_lseek(handle, 0, SEEK_CUR);\n\t\tif (tcmd_do_write_check(handle, options->data, options->size))\n\t\t\treturn -1;\n\t}\n\n\treturn write_options_end(handle, offset);\n}\n\n/**\n * tcmd_get_options - Get the current options from the output file handle\n * @handle: The output file descriptor that has options.\n * @len: Returns the length of the buffer allocated and returned.\n *\n * Reads the options that have not been written to the file yet,\n * puts them into an allocated buffer and sets @len to the size\n * added. Used by trace-msg.c to send options over the network.\n *\n * Note, the options cannot be referenced again once this is called.\n *  New options can be added and referenced.\n *\n * Returns an allocated buffer (must be freed with free()) that contains\n *   the options to send, with @len set to the size of the content.\n *   NULL on error (and @len is undefined).\n */\n__hidden void *tcmd_get_options(struct tracecmd_output *handle, size_t *len)\n{\n\tstruct tracecmd_msg_handle msg_handle;\n\tstruct tracecmd_output out_handle;\n\tstruct tracecmd_option *options;\n\tunsigned short endian2;\n\tunsigned int endian4;\n\ttsize_t offset;\n\tvoid *buf = NULL;\n\n\t/* Use the msg_cache as our output */\n\tmemset(&msg_handle, 0, sizeof(msg_handle));\n\tmsg_handle.cfd = -1;\n\tif (tracecmd_msg_handle_cache(&msg_handle) < 0)\n\t\treturn NULL;\n\n\tout_handle = *handle;\n\tout_handle.fd = msg_handle.cfd;\n\tout_handle.msg_handle = &msg_handle;\n\n\tlist_for_each_entry(options, &handle->options, list) {\n\t\t/* Option is already saved, skip it */\n\t\tif (options->offset)\n\t\t\tcontinue;\n\t\tendian2 = convert_endian_2(handle, options->id);\n\t\tif (tcmd_do_write_check(&out_handle, &endian2, 2))\n\t\t\tgoto out;\n\t\tendian4 = convert_endian_4(handle, options->size);\n\t\tif (tcmd_do_write_check(&out_handle, &endian4, 4))\n\t\t\tgoto out;\n\t\t/* The option can not be referenced again */\n\t\toptions->offset = -1;\n\t\tif (tcmd_do_write_check(&out_handle, options->data, options->size))\n\t\t\tgoto out;\n\t}\n\n\toffset = do_lseek(&out_handle, 0, SEEK_CUR);\n\tif (offset == (off_t)-1)\n\t\tgoto out;\n\tbuf = malloc(offset);\n\tif (!buf)\n\t\tgoto out;\n\n\tif (do_lseek(&out_handle, 0, SEEK_SET) == (off_t)-1)\n\t\tgoto out;\n\t*len = read(msg_handle.cfd, buf, offset);\n\tif (*len != offset) {\n\t\tfree(buf);\n\t\tbuf = NULL;\n\t}\n\n out:\n\tclose(msg_handle.cfd);\n\treturn buf;\n}\n\n/**\n * tcmd_append_options - Append options to the file\n * @handle: The output file descriptor that has options.\n * @buf: The options to append.\n * @len: The length of @buf.\n *\n * Will add an options section header for the content of @buf to\n * be written as options into the @handle.\n * Used by trace-msg.c to retrieve options over the network.\n *\n * Returns 0 on success and -1 on error.\n */\n__hidden int tcmd_append_options(struct tracecmd_output *handle, void *buf,\n\t\t\t\t size_t len)\n{\n\ttsize_t offset;\n\n\toffset = write_options_start(handle);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\tif (tcmd_do_write_check(handle, buf, len))\n\t\treturn -1;\n\n\treturn write_options_end(handle, offset);\n}\n\nint tracecmd_write_meta_strings(struct tracecmd_output *handle)\n{\n\tif (!HAS_SECTIONS(handle))\n\t\treturn 0;\n\n\treturn save_string_section(handle);\n}\n\nint tracecmd_write_options(struct tracecmd_output *handle)\n{\n\tif (!HAS_SECTIONS(handle))\n\t\treturn write_options_v6(handle);\n\treturn write_options(handle);\n}\n\nstatic int append_options_v6(struct tracecmd_output *handle)\n{\n\tstruct tracecmd_option *options;\n\tunsigned short option;\n\tunsigned short endian2;\n\tunsigned int endian4;\n\toff_t offset;\n\tint r;\n\n\t/*\n\t * We can append only if options are already written and tracing data\n\t * is not yet written\n\t */\n\tif (handle->file_state != TRACECMD_FILE_OPTIONS)\n\t\treturn -1;\n\n\tif (do_lseek(handle, 0, SEEK_END) == (off_t)-1)\n\t\treturn -1;\n\toffset = do_lseek(handle, -2, SEEK_CUR);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\tr = do_preed(handle, &option, 2, offset);\n\tif (r != 2 || option != TRACECMD_OPTION_DONE)\n\t\treturn -1;\n\n\tlist_for_each_entry(options, &handle->options, list) {\n\t\tendian2 = convert_endian_2(handle, options->id);\n\t\tif (tcmd_do_write_check(handle, &endian2, 2))\n\t\t\treturn -1;\n\n\t\tendian4 = convert_endian_4(handle, options->size);\n\t\tif (tcmd_do_write_check(handle, &endian4, 4))\n\t\t\treturn -1;\n\n\t\t/* Save the data location in case it needs to be updated */\n\t\toptions->offset = do_lseek(handle, 0, SEEK_CUR);\n\n\t\tif (tcmd_do_write_check(handle, options->data,\n\t\t\t\t   options->size))\n\t\t\treturn -1;\n\t}\n\n\toption = TRACECMD_OPTION_DONE;\n\n\tif (tcmd_do_write_check(handle, &option, 2))\n\t\treturn -1;\n\n\treturn 0;\n}\n\nint tracecmd_append_options(struct tracecmd_output *handle)\n{\n\tif (!HAS_SECTIONS(handle))\n\t\treturn append_options_v6(handle);\n\treturn write_options(handle);\n}\n\nstatic struct tracecmd_option *\nadd_buffer_option_v6(struct tracecmd_output *handle, const char *name, int cpus)\n{\n\tstruct tracecmd_option *option;\n\tchar *buf;\n\tint size = 8 + strlen(name) + 1;\n\n\tbuf = calloc(1, size);\n\tif (!buf) {\n\t\ttracecmd_warning(\"Failed to malloc buffer\");\n\t\treturn NULL;\n\t}\n\t*(tsize_t *)buf = 0;\n\tstrcpy(buf + 8, name);\n\n\toption = tracecmd_add_option(handle, TRACECMD_OPTION_BUFFER, size, buf);\n\tfree(buf);\n\n\t/*\n\t * In case a buffer instance has different number of CPUs as the\n\t * local machine.\n\t */\n\tif (cpus)\n\t\ttracecmd_add_option(handle, TRACECMD_OPTION_CPUCOUNT,\n\t\t\t\t    sizeof(int), &cpus);\n\n\treturn option;\n}\n\nint tracecmd_add_buffer_info(struct tracecmd_output *handle, const char *name, int cpus)\n{\n\tstruct tracecmd_buffer *buf;\n\n\tbuf = calloc(1, sizeof(struct tracecmd_buffer));\n\tif (!buf)\n\t\treturn -1;\n\tbuf->name = strdup(name);\n\tbuf->cpus = cpus;\n\tif (!buf->name) {\n\t\tfree(buf);\n\t\treturn -1;\n\t}\n\tlist_add_tail(&buf->list, &handle->buffers);\n\treturn 0;\n}\n\nint tracecmd_write_buffer_info(struct tracecmd_output *handle)\n{\n\tstruct tracecmd_option *option;\n\tstruct tracecmd_buffer *buf;\n\n\tif (HAS_SECTIONS(handle))\n\t\treturn 0;\n\n\tlist_for_each_entry(buf, &handle->buffers, list) {\n\t\toption = add_buffer_option_v6(handle, buf->name, buf->cpus);\n\t\tif (!option)\n\t\t\treturn -1;\n\t\tbuf->option = option;\n\t}\n\n\treturn 0;\n}\n\nstatic tsize_t get_buffer_file_offset(struct tracecmd_output *handle, const char *name)\n{\n\tstruct tracecmd_buffer *buf;\n\n\tlist_for_each_entry(buf, &handle->buffers, list) {\n\t\tif (!strcmp(name, buf->name)) {\n\t\t\tif (!buf->option)\n\t\t\t\tbreak;\n\t\t\treturn buf->option->offset;\n\t\t}\n\t}\n\treturn 0;\n}\n\nint tracecmd_write_cmdlines(struct tracecmd_output *handle)\n{\n\ttsize_t offset;\n\tint ret;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_CMD_LINES)) {\n\t\ttracecmd_warning(\"Cannot write command lines into the file, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn -1;\n\t}\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_CMDLINES,\n\t\t\t\t\t       \"command lines\", true);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\ttcmd_out_compression_start(handle);\n\n\tret = save_tracing_file_data(handle, \"saved_cmdlines\");\n\tif (ret < 0) {\n\t\ttcmd_out_compression_reset(handle);\n\t\treturn ret;\n\t}\n\n\tif (tcmd_out_compression_end(handle))\n\t\treturn -1;\n\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\treturn -1;\n\n\thandle->file_state = TRACECMD_FILE_CMD_LINES;\n\treturn 0;\n}\n\n#define MODULES_FILE \"/proc/modules\"\n\nint tracecmd_append_modules_file(struct tracecmd_output *handle)\n{\n\ttsize_t offset;\n\tstruct stat st;\n\tint ret;\n\n\tif (!HAS_SECTIONS(handle))\n\t\treturn -1;\n\n\tret = stat(MODULES_FILE, &st);\n\tif (ret < 0)\n\t\treturn -1;\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_MODULES_FILE, \"modules\", true);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\ttcmd_out_compression_start(handle);\n\n\tcopy_file(handle, MODULES_FILE);\n\n\tif (tcmd_out_compression_end(handle))\n\t\treturn -1;\n\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\treturn -1;\n\n\treturn 0;\n}\n\n#define BTF_FILE \"/sys/kernel/btf/vmlinux\"\n\nint tracecmd_append_btf_file(struct tracecmd_output *handle)\n{\n\ttsize_t offset, size, check_size;\n\tstruct tracecmd_option *option;\n\tstruct iovec *vect;\n\tstruct stat st;\n\tint ret;\n\n\tif (!HAS_SECTIONS(handle))\n\t\treturn -1;\n\n\tret = stat(BTF_FILE, &st);\n\tif (ret < 0)\n\t\treturn -1;\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_BTF_FILE, \"BTF\", true);\n\tif (offset == (off_t)-1)\n\t\treturn -1;\n\n\ttcmd_out_compression_start(handle);\n\n\tcheck_size = copy_file(handle, BTF_FILE);\n\tif (st.st_size != check_size) {\n\t\terrno = EINVAL;\n\t\ttracecmd_warning(\"error in size of file '%s'\", BTF_FILE);\n\t\treturn -1;\n\t}\n\n\tif (tcmd_out_compression_end(handle))\n\t\treturn -1;\n\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\treturn -1;\n\n\treturn 0;\n\n\t/*\n\t * BTF file\n\t *  - data offset in the file\n\t *  - data size\n\t */\n\n\tvect = calloc(2, sizeof(struct iovec));\n\tif (!vect)\n\t\treturn -1;\n\tvect[0].iov_base = &offset;\n\tvect[0].iov_len = 8;\n\tvect[1].iov_base = &size;\n\tvect[1].iov_len = 8;\n\n\toption = tracecmd_add_option_v(handle, TRACECMD_OPTION_BTF_FILE, vect, 2);\n\tfree(vect);\n\n\tif (!option)\n\t\treturn -1;\n\n\tif (do_lseek(handle, 0, SEEK_END) == (off_t)-1)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic char *get_clock(struct tracecmd_output *handle)\n{\n\tstruct tracefs_instance *inst;\n\n\tif (handle->trace_clock)\n\t\treturn handle->trace_clock;\n\n\t/*\n\t * If no clock is set on this handle, get the trace clock of\n\t * the top instance in the handle's tracing dir\n\t */\n\tif (!handle->tracing_dir) {\n\t\thandle->trace_clock = tracefs_get_clock(NULL);\n\t\treturn handle->trace_clock;\n\t}\n\n\tinst = tracefs_instance_alloc(handle->tracing_dir, NULL);\n\tif (!inst)\n\t\treturn NULL;\n\thandle->trace_clock = tracefs_get_clock(inst);\n\ttracefs_instance_free(inst);\n\treturn handle->trace_clock;\n}\n\n__hidden struct tracecmd_option *\ntcmd_out_add_buffer_option(struct tracecmd_output *handle, const char *name,\n\t\t\t   unsigned short id, unsigned long long data_offset,\n\t\t\t   int cpus, struct data_file_write *cpu_data, int page_size)\n{\n\tstruct tracecmd_option *option;\n\tint i, j = 0, k = 0;\n\tint *cpu_ids = NULL;\n\tstruct iovec *vect;\n\tchar *clock;\n\n\tif (!HAS_SECTIONS(handle))\n\t\treturn NULL;\n\n\tclock = get_clock(handle);\n\tif (!clock) {\n\t\ttracecmd_warning(\"Could not find clock, set to 'local'\");\n\t\tclock = \"local\";\n\t}\n\n\t/*\n\t * Buffer flyrecord option:\n\t *  - trace data offset in the file\n\t *  - buffer name\n\t *  - buffer clock\n\t *  - page size\n\t *  - CPU count\n\t *  - for each CPU:\n\t *    - CPU id\n\t *    - CPU trace data offset in the file\n\t *    - CPU trace data size\n\t */\n\n\t/*\n\t * Buffer latency option:\n\t *  - trace data offset in the file\n\t *  - buffer name\n\t *  - buffer clock\n\t */\n\n\t/*\n\t * 5 : offset, name, clock, page size, count\n\t * 3 : cpu offset, name, clock\n\t */\n\tvect = calloc(5 + (cpus * 3), sizeof(struct iovec));\n\tif (!vect)\n\t\treturn NULL;\n\tif (cpus) {\n\t\tcpu_ids = calloc(cpus, sizeof(int));\n\t\tif (!cpu_ids) {\n\t\t\tfree(vect);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tvect[j].iov_base = (void *) &data_offset;\n\tvect[j++].iov_len = 8;\n\tvect[j].iov_base = (void *) name;\n\tvect[j++].iov_len = strlen(name) + 1;\n\tvect[j].iov_base = (void *) clock;\n\tvect[j++].iov_len = strlen(clock) + 1;\n\tif (id == TRACECMD_OPTION_BUFFER) {\n\t\tvect[j].iov_base = &page_size;\n\t\tvect[j++].iov_len = 4;\n\t\tvect[j].iov_base = (void *) &k;\n\t\tvect[j++].iov_len = 4;\n\t\tfor (i = 0; i < cpus; i++) {\n\t\t\tif (!cpu_data[i].file_size)\n\t\t\t\tcontinue;\n\t\t\tcpu_ids[i] = i;\n\t\t\tvect[j].iov_base = &cpu_ids[i];\n\t\t\tvect[j++].iov_len = 4;\n\t\t\tvect[j].iov_base = &cpu_data[i].data_offset;\n\t\t\tvect[j++].iov_len = 8;\n\t\t\tvect[j].iov_base = &cpu_data[i].write_size;\n\t\t\tvect[j++].iov_len = 8;\n\t\t\tk++;\n\t\t}\n\t}\n\n\toption = tracecmd_add_option_v(handle, id, vect, j);\n\tfree(vect);\n\tfree(cpu_ids);\n\n\treturn option;\n}\n\nstruct tracecmd_output *tracecmd_create_file_latency(const char *output_file, int cpus,\n\t\t\t\t\t\t     int file_version, const char *compression)\n{\n\tstruct tracecmd_output *handle;\n\ttsize_t offset;\n\tchar *path;\n\n\thandle = tracecmd_output_create(output_file);\n\tif (!handle)\n\t\treturn NULL;\n\n\tif (file_version && tracecmd_output_set_version(handle, file_version))\n\t\tgoto out_free;\n\n\tif (compression) {\n\t\tif (tracecmd_output_set_compression(handle, compression))\n\t\t\tgoto out_free;\n\t} else if (file_version >= FILE_VERSION_COMPRESSION) {\n\t\ttracecmd_output_set_compression(handle, \"any\");\n\t}\n\n\tif (tracecmd_output_write_headers(handle, NULL))\n\t\tgoto out_free;\n\t/*\n\t * Save the command lines;\n\t */\n\tif (tracecmd_write_cmdlines(handle) < 0)\n\t\tgoto out_free;\n\n\tif (tracecmd_write_cpus(handle, cpus) < 0)\n\t\tgoto out_free;\n\tif (tracecmd_write_buffer_info(handle) < 0)\n\t\tgoto out_free;\n\tif (tracecmd_write_options(handle) < 0)\n\t\tgoto out_free;\n\n\tif (!tcmd_check_out_state(handle, TRACECMD_FILE_CPU_LATENCY)) {\n\t\ttracecmd_warning(\"Cannot write latency data into the file, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\tgoto out_free;\n\t}\n\n\tif (!HAS_SECTIONS(handle) && tcmd_do_write_check(handle, \"latency  \", 10))\n\t\tgoto out_free;\n\n\tpath = get_tracing_file(handle, \"trace\");\n\tif (!path)\n\t\tgoto out_free;\n\n\toffset = do_lseek(handle, 0, SEEK_CUR);\n\tif (HAS_SECTIONS(handle) &&\n\t    !tcmd_out_add_buffer_option(handle, \"\", TRACECMD_OPTION_BUFFER_TEXT,\n\t\t\t\t   offset, 0, NULL, getpagesize()))\n\t\tgoto out_free;\n\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_BUFFER_TEXT,\n\t\t\t\t\t       \"buffer latency\", false);\n\n\tcopy_file_compress(handle, path, NULL);\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\tgoto out_free;\n\n\tput_tracing_file(path);\n\n\thandle->file_state = TRACECMD_FILE_CPU_LATENCY;\n\n\tif (HAS_SECTIONS(handle))\n\t\ttracecmd_write_options(handle);\n\n\treturn handle;\n\nout_free:\n\ttracecmd_output_close(handle);\n\treturn NULL;\n}\n\nstatic int save_clock(struct tracecmd_output *handle, char *clock)\n{\n\tunsigned long long endian8;\n\tchar *str = NULL;\n\tint ret;\n\n\tret = asprintf(&str, \"[%s]\", clock);\n\tif (ret < 0)\n\t\treturn -1;\n\n\tendian8 = convert_endian_8(handle, strlen(str));\n\tret = tcmd_do_write_check(handle, &endian8, 8);\n\tif (ret)\n\t\tgoto out;\n\tret = tcmd_do_write_check(handle, str, strlen(str));\n\nout:\n\tfree(str);\n\treturn ret;\n}\n\nstatic int update_buffer_cpu_offset_v6(struct tracecmd_output *handle,\n\t\t\t\t       const char *name, tsize_t offset)\n{\n\ttsize_t b_offset;\n\ttsize_t current;\n\n\tif (!name)\n\t\tname = \"\";\n\n\tb_offset = get_buffer_file_offset(handle, name);\n\tif (!b_offset) {\n\t\ttracecmd_warning(\"Cannot find description for buffer %s\", name);\n\t\treturn -1;\n\t}\n\n\tcurrent = do_lseek(handle, 0, SEEK_CUR);\n\n\t/* Go to the option data, where will write the offest */\n\tif (do_lseek(handle, b_offset, SEEK_SET) == (off_t)-1) {\n\t\ttracecmd_warning(\"could not seek to %lld\", b_offset);\n\t\treturn -1;\n\t}\n\n\tif (tcmd_do_write_check(handle, &offset, 8))\n\t\treturn -1;\n\n\t/* Go back to end of file */\n\tif (do_lseek(handle, current, SEEK_SET) == (off_t)-1) {\n\t\ttracecmd_warning(\"could not seek to %lld\", offset);\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\n__hidden int tcmd_out_write_emty_cpu_data(struct tracecmd_output *handle, int cpus)\n{\n\tunsigned long long zero = 0;\n\tchar *clock;\n\tint ret;\n\tint i;\n\n\tif (HAS_SECTIONS(handle))\n\t\treturn 0;\n\n\tret = handle->file_state == TRACECMD_FILE_CPU_FLYRECORD ? 0 :\n\t\t\t\t    tcmd_check_file_state(handle->file_version,\n\t\t\t\t\t\t\t  handle->file_state,\n\t\t\t\t\t\t\t  TRACECMD_FILE_CPU_FLYRECORD);\n\tif (ret < 0) {\n\t\ttracecmd_warning(\"Cannot write trace data into the file, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\treturn ret;\n\t}\n\n\tif (tcmd_do_write_check(handle, \"flyrecord\", 10))\n\t\treturn -1;\n\n\tfor (i = 0; i < cpus; i++) {\n\t\t/* Write 0 for trace data offset and size */\n\t\tif (tcmd_do_write_check(handle, &zero, 8))\n\t\t\treturn -1;\n\n\t\tif (tcmd_do_write_check(handle, &zero, 8))\n\t\t\treturn -1;\n\t}\n\tclock = get_clock(handle);\n\tif (clock && save_clock(handle, clock))\n\t\treturn -1;\n\n\thandle->file_state = TRACECMD_FILE_CPU_FLYRECORD;\n\treturn 0;\n}\n\n__hidden int tcmd_out_write_cpu_data(struct tracecmd_output *handle,\n\t\t\t\t     int cpus, struct cpu_data_source *data,\n\t\t\t\t     const char *buff_name)\n{\n\tstruct data_file_write *data_files = NULL;\n\ttsize_t data_offs, offset;\n\tunsigned long long endian8;\n\tunsigned long long read_size;\n\tint page_size;\n\tchar *clock;\n\tchar *str;\n\tint ret;\n\tint i;\n\n\t/* This can be called multiple times (when recording instances) */\n\tret = handle->file_state == TRACECMD_FILE_CPU_FLYRECORD ? 0 :\n\t\t\t\t    tcmd_check_file_state(handle->file_version,\n\t\t\t\t\t\t\t  handle->file_state,\n\t\t\t\t\t\t\t  TRACECMD_FILE_CPU_FLYRECORD);\n\tif (ret < 0) {\n\t\ttracecmd_warning(\"Cannot write trace data into the file, unexpected state 0x%X\",\n\t\t\t\t handle->file_state);\n\t\tgoto out_free;\n\t}\n\n\tif (*buff_name == '\\0')\n\t\tpage_size = handle->page_size;\n\telse\n\t\tpage_size = get_trace_page_size(handle, buff_name);\n\n\tdata_offs = do_lseek(handle, 0, SEEK_CUR);\n\tif (!HAS_SECTIONS(handle) && tcmd_do_write_check(handle, \"flyrecord\", 10))\n\t\tgoto out_free;\n\n\tif (asprintf(&str, \"buffer flyrecord %s\", buff_name) < 1)\n\t\tgoto out_free;\n\toffset = write_compress_section_header(handle, TRACECMD_OPTION_BUFFER, str, false);\n\tfree(str);\n\tif (offset == (off_t)-1)\n\t\tgoto out_free;\n\n\tdata_files = calloc(cpus, sizeof(*data_files));\n\tif (!data_files)\n\t\tgoto out_free;\n\n\tfor (i = 0; i < cpus; i++) {\n\t\tdata_files[i].file_size = data[i].size;\n\t\t/*\n\t\t * Place 0 for the data offset and size, and save the offsets to\n\t\t * updated them with the correct data later.\n\t\t */\n\t\tif (!HAS_SECTIONS(handle)) {\n\t\t\tendian8 = 0;\n\t\t\tdata_files[i].file_data_offset = do_lseek(handle, 0, SEEK_CUR);\n\t\t\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\t\t\tgoto out_free;\n\t\t\tdata_files[i].file_write_size = do_lseek(handle, 0, SEEK_CUR);\n\t\t\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\t\t\tgoto out_free;\n\t\t}\n\t}\n\n\tif (!HAS_SECTIONS(handle)) {\n\t\tupdate_buffer_cpu_offset_v6(handle, buff_name, data_offs);\n\t\tclock = get_clock(handle);\n\t\tif (clock && save_clock(handle, clock))\n\t\t\tgoto out_free;\n\t}\n\n\tfor (i = 0; i < cpus; i++) {\n\t\tdata_files[i].data_offset = do_lseek(handle, 0, SEEK_CUR);\n\t\t/* Page align offset */\n\t\tdata_files[i].data_offset += page_size - 1;\n\t\tdata_files[i].data_offset &= ~(page_size - 1);\n\n\t\tret = do_lseek(handle, data_files[i].data_offset, SEEK_SET);\n\t\tif (ret == (off_t)-1)\n\t\t\tgoto out_free;\n\n\t\tif (!tracecmd_get_quiet(handle))\n\t\t\tfprintf(stderr, \"CPU%d data recorded at offset=0x%llx\\n\",\n\t\t\t\ti, (unsigned long long)data_files[i].data_offset);\n\n\t\tif (data[i].size) {\n\t\t\tif (lseek(data[i].fd, data[i].offset, SEEK_SET) == (off_t)-1)\n\t\t\t\tgoto out_free;\n\t\t\tread_size = tcmd_out_copy_fd_compress(handle, data[i].fd,\n\t\t\t\t\t\t\t      data[i].size, &data_files[i].write_size,\n\t\t\t\t\t\t\t      page_size);\n\n\t\t\tif (read_size != data_files[i].file_size) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\ttracecmd_warning(\"did not match size of %llu to %llu\",\n\t\t\t\t\t\t read_size, data_files[i].file_size);\n\t\t\t\tgoto out_free;\n\t\t\t}\n\t\t} else {\n\t\t\tdata_files[i].write_size = 0;\n\t\t}\n\n\t\tif (!HAS_SECTIONS(handle)) {\n\t\t\t/* Write the real CPU data offset in the file */\n\t\t\tif (do_lseek(handle, data_files[i].file_data_offset, SEEK_SET) == (off_t)-1)\n\t\t\t\tgoto out_free;\n\t\t\tendian8 = convert_endian_8(handle, data_files[i].data_offset);\n\t\t\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\t\t\tgoto out_free;\n\t\t\t/* Write the real CPU data size in the file */\n\t\t\tif (do_lseek(handle, data_files[i].file_write_size, SEEK_SET) == (off_t)-1)\n\t\t\t\tgoto out_free;\n\t\t\tendian8 = convert_endian_8(handle, data_files[i].write_size);\n\t\t\tif (tcmd_do_write_check(handle, &endian8, 8))\n\t\t\t\tgoto out_free;\n\t\t\toffset = data_files[i].data_offset + data_files[i].write_size;\n\t\t\tif (do_lseek(handle, offset, SEEK_SET) == (off_t)-1)\n\t\t\t\tgoto out_free;\n\t\t}\n\t\tif (!tracecmd_get_quiet(handle)) {\n\t\t\tfprintf(stderr, \"    %llu bytes in size\",\n\t\t\t\t(unsigned long long)data_files[i].write_size);\n\t\t\tif (handle->compress)\n\t\t\t\tfprintf(stderr, \" (%llu uncompressed)\",\n\t\t\t\t\t(unsigned long long)data_files[i].file_size);\n\t\t\tfprintf(stderr, \"\\n\");\n\t\t}\n\t}\n\n\tif (HAS_SECTIONS(handle) &&\n\t    !tcmd_out_add_buffer_option(handle, buff_name,  TRACECMD_OPTION_BUFFER,\n\t\t\t\t\tdata_offs, cpus, data_files, page_size))\n\t\tgoto out_free;\n\n\tfree(data_files);\n\tif (do_lseek(handle, 0, SEEK_END) == (off_t)-1)\n\t\treturn -1;\n\n\tif (tcmd_out_update_section_header(handle, offset))\n\t\tgoto out_free;\n\n\thandle->file_state = TRACECMD_FILE_CPU_FLYRECORD;\n\n\tif (HAS_SECTIONS(handle))\n\t\ttracecmd_write_options(handle);\n\n\treturn 0;\n\n out_free:\n\tdo_lseek(handle, 0, SEEK_END);\n\tfree(data_files);\n\treturn -1;\n}\n\nint tracecmd_write_cpu_data(struct tracecmd_output *handle,\n\t\t\t    int cpus, char * const *cpu_data_files, const char *buff_name)\n{\n\tstruct cpu_data_source *data;\n\tstruct stat st;\n\tint size = 0;\n\tint ret;\n\tint i;\n\n\tif (!buff_name)\n\t\tbuff_name = \"\";\n\n\tdata = calloc(cpus, sizeof(struct cpu_data_source));\n\tif (!data)\n\t\treturn -1;\n\n\tfor (i = 0; i < cpus; i++) {\n\t\tret = stat(cpu_data_files[i], &st);\n\t\tif (ret < 0) {\n\t\t\ttracecmd_warning(\"can not stat '%s'\", cpu_data_files[i]);\n\t\t\tbreak;\n\t\t}\n\t\tdata[i].fd = open(cpu_data_files[i], O_RDONLY);\n\t\tif (data[i].fd < 0) {\n\t\t\ttracecmd_warning(\"Can't read '%s'\", data[i].fd);\n\t\t\tbreak;\n\t\t}\n\n\t\tdata[i].size = st.st_size;\n\t\tdata[i].offset = 0;\n\t\tsize += st.st_size;\n\t}\n\n\tif (i < cpus)\n\t\tret = -1;\n\telse\n\t\tret = tcmd_out_write_cpu_data(handle, cpus, data, buff_name);\n\n\tfor (i--; i >= 0; i--)\n\t\tclose(data[i].fd);\n\n\tfree(data);\n\treturn ret;\n}\n\nint tracecmd_append_cpu_data(struct tracecmd_output *handle,\n\t\t\t     int cpus, char * const *cpu_data_files)\n{\n\tint ret;\n\n\tret = tracecmd_write_cpus(handle, cpus);\n\tif (ret)\n\t\treturn ret;\n\tret = tracecmd_write_buffer_info(handle);\n\tif (ret)\n\t\treturn ret;\n\tret = tracecmd_write_options(handle);\n\tif (ret)\n\t\treturn ret;\n\n\treturn tracecmd_write_cpu_data(handle, cpus, cpu_data_files, NULL);\n}\n\nint tracecmd_append_buffer_cpu_data(struct tracecmd_output *handle,\n\t\t\t\t    const char *name, int cpus, char * const *cpu_data_files)\n{\n\treturn tracecmd_write_cpu_data(handle, cpus, cpu_data_files, name);\n}\n\nstruct tracecmd_output *tracecmd_get_output_handle_fd(int fd)\n{\n\tstruct tracecmd_output *handle = NULL;\n\tstruct tracecmd_input *ihandle;\n\tconst char *cname = NULL;\n\tconst char *cver = NULL;\n\tint fd2;\n\n\t/* Move the file descriptor to the beginning */\n\tif (lseek(fd, 0, SEEK_SET) == (off_t)-1)\n\t\treturn NULL;\n\n\t/* dup fd to be used by the ihandle bellow */\n\tfd2 = dup(fd);\n\tif (fd2 < 0)\n\t\treturn NULL;\n\n\t/* get a input handle from this */\n\tihandle = tracecmd_alloc_fd(fd2, TRACECMD_FL_LOAD_NO_PLUGINS);\n\tif (!ihandle)\n\t\treturn NULL;\n\ttracecmd_read_headers(ihandle, 0);\n\n\t/* move the file descriptor to the end */\n\tif (lseek(fd, 0, SEEK_END) == (off_t)-1)\n\t\tgoto out_free;\n\n\t/* create a partial output handle */\n\thandle = calloc(1, sizeof(*handle));\n\tif (!handle)\n\t\tgoto out_free;\n\n\thandle->fd = fd;\n\n\t/* get tep, state, endian and page size */\n\thandle->file_state = tracecmd_get_file_state(ihandle);\n\t/* Use the tep of the ihandle for later writes */\n\thandle->pevent = tracecmd_get_tep(ihandle);\n\ttep_ref(handle->pevent);\n\thandle->page_size = tracecmd_page_size(ihandle);\n\thandle->file_version = tracecmd_get_in_file_version(ihandle);\n\thandle->options_start = tcmd_get_last_option_offset(ihandle);\n\thandle->strings_offs = tcmd_get_meta_strings_size(ihandle);\n\tlist_head_init(&handle->options);\n\tlist_head_init(&handle->buffers);\n\n\tif (!tracecmd_get_file_compress_proto(ihandle, &cname, &cver)) {\n\t\thandle->compress = tracecmd_compress_alloc(cname, cver, handle->fd,\n\t\t\t\t\t\t\t   handle->pevent, handle->msg_handle);\n\t\tif (!handle->compress)\n\t\t\tgoto out_free;\n\t}\n\ttracecmd_close(ihandle);\n\n\treturn handle;\n\n out_free:\n\ttracecmd_close(ihandle);\n\tfree(handle);\n\treturn NULL;\n}\n\n/**\n * tracecmd_output_create - Create new output handle to a trace file with given name\n * @output_file: Name of the trace file that will be created.\n *\n * The @output_file parameter can be NULL. In this case the output handle is created\n * and initialized, but is not associated with a file.\n *\n * Returns pointer to created outpuy handle, or NULL in case of an error.\n */\nstruct tracecmd_output *tracecmd_output_create(const char *output_file)\n{\n\tstruct tracecmd_output *out;\n\tint fd = -1;\n\n\tif (output_file) {\n\t\tfd = open(output_file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);\n\t\tif (fd < 0)\n\t\t\treturn NULL;\n\t}\n\tout = tracecmd_output_create_fd(fd);\n\tif (!out && fd >= 0) {\n\t\tclose(fd);\n\t\tunlink(output_file);\n\t}\n\n\treturn out;\n}\n\n/**\n * tracecmd_copy - copy the headers of one trace.dat file for another\n * @ihandle: input handle of the trace.dat file to copy\n * @file: the trace.dat file to create\n * @state: what data will be copied from the source handle\n * @file_version: version of the output file\n * @compression: compression of the output file, can be one of:\n *\t\t NULL - inherit compression from the input file\n *\t\t \"any\" - compress the output file with the best available algorithm\n *\t\t \"none\" - do not compress the output file\n *\t\t algorithm_name - compress the output file with specified algorithm\n *\n * Reads the header information and creates a new trace data file\n * with the same characteristics (events and all) and returns\n * tracecmd_output handle to this new file.\n */\nstruct tracecmd_output *tracecmd_copy(struct tracecmd_input *ihandle, const char *file,\n\t\t\t\t      enum tracecmd_file_states state, int file_version,\n\t\t\t\t      const char *compression)\n{\n\tenum tracecmd_file_states fstate;\n\tstruct tracecmd_output *handle;\n\n\thandle = tracecmd_output_create(file);\n\tif (!handle)\n\t\treturn NULL;\n\n\tif (tracecmd_output_set_from_input(handle, ihandle))\n\t\tgoto out_free;\n\n\tif (file_version >= FILE_VERSION_MIN)\n\t\ttracecmd_output_set_version(handle, file_version);\n\n\tif (compression && tracecmd_output_set_compression(handle, compression))\n\t\tgoto out_free;\n\n\toutput_write_init(handle);\n\tfstate = state > TRACECMD_FILE_CPU_COUNT ? TRACECMD_FILE_CPU_COUNT : state;\n\tif (tracecmd_copy_headers(ihandle, handle, 0, fstate) < 0)\n\t\tgoto out_free;\n\n\tif (tracecmd_copy_buffer_descr(ihandle, handle) < 0)\n\t\tgoto out_free;\n\n\tif (state >= TRACECMD_FILE_OPTIONS &&\n\t    tracecmd_copy_options(ihandle, handle) < 0)\n\t\tgoto out_free;\n\n\tif (state >= TRACECMD_FILE_CPU_LATENCY &&\n\t    tcmd_copy_trace_data(ihandle, handle) < 0)\n\t\tgoto out_free;\n\n\tif (HAS_SECTIONS(handle))\n\t\ttracecmd_write_options(handle);\n\n\t/* The file is all ready to have cpu data attached */\n\treturn handle;\n\nout_free:\n\tif (handle)\n\t\ttracecmd_output_close(handle);\n\n\tunlink(file);\n\treturn NULL;\n}\n\n__hidden void tcmd_out_set_file_state(struct tracecmd_output *handle, int new_state)\n{\n\thandle->file_state = new_state;\n}\n\n__hidden bool tcmd_check_out_state(struct tracecmd_output *handle, int new_state)\n{\n\treturn tcmd_check_file_state(handle->file_version, handle->file_state, new_state);\n}\n\n__hidden int tcmd_out_save_options_offset(struct tracecmd_output *handle, unsigned long long start)\n{\n\tunsigned long long new, en8;\n\n\tif (HAS_SECTIONS(handle)) {\n\t\t/* Append to the previous options section, if any */\n\t\tif (!handle->options_start)\n\t\t\treturn -1;\n\n\t\tnew = do_lseek(handle, 0, SEEK_CUR);\n\t\tif (do_lseek(handle, handle->options_start, SEEK_SET) == (off_t)-1)\n\t\t\treturn -1;\n\n\t\ten8 = convert_endian_8(handle, start);\n\t\tif (tcmd_do_write_check(handle, &en8, 8))\n\t\t\treturn -1;\n\n\t\thandle->options_start = new;\n\t\tif (do_lseek(handle, new, SEEK_SET) == (off_t)-1)\n\t\t\treturn -1;\n\t} else {\n\t\thandle->options_start = start;\n\t}\n\n\treturn 0;\n}\n\n/**\n * tracecmd_get_out_file_version - return the trace.dat file version\n * @handle: output handle for the trace.dat file\n */\nunsigned long tracecmd_get_out_file_version(struct tracecmd_output *handle)\n{\n\treturn handle->file_version;\n}\n\nsize_t tracecmd_get_out_file_offset(struct tracecmd_output *handle)\n{\n\treturn do_lseek(handle, 0, SEEK_CUR);\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-perf.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n *\n */\n#include <unistd.h>\n#include <sys/syscall.h>\n#include <sys/mman.h>\n\n#include \"trace-cmd-private.h\"\n\nstatic void default_perf_init_pe(struct perf_event_attr *pe)\n{\n\tpe->type = PERF_TYPE_SOFTWARE;\n\tpe->sample_type = PERF_SAMPLE_CPU;\n\tpe->size = sizeof(struct perf_event_attr);\n\tpe->config = PERF_COUNT_HW_CPU_CYCLES;\n\tpe->disabled = 1;\n\tpe->exclude_kernel = 1;\n\tpe->freq = 1;\n\tpe->sample_freq = 1000;\n\tpe->inherit = 1;\n\tpe->mmap = 1;\n\tpe->comm = 1;\n\tpe->task = 1;\n\tpe->precise_ip = 1;\n\tpe->sample_id_all = 1;\n\tpe->read_format = PERF_FORMAT_ID |\n\t\t\tPERF_FORMAT_TOTAL_TIME_ENABLED |\n\t\t\tPERF_FORMAT_TOTAL_TIME_RUNNING;\n}\n\n/**\n * tcmd_perf_init - Initialize perf context\n *\n * @perf: structure, representing perf context, that will be initialized.\n * @pages: Number of perf memory mapped pages.\n * @cpu: CPU number, associated with this perf context.\n * @pid: PID, associated with this perf context.\n *\n * The perf context in initialized with default values. The caller can set\n * custom perf parameters in perf->pe, before calling tcmd_perf_open() API.\n *\n * Returns 0 on success, or -1 in case of an error.\n *\n */\nint __hidden tcmd_perf_init(struct trace_perf *perf, int pages, int cpu, int pid)\n{\n\tif (!perf)\n\t\treturn -1;\n\n\tmemset(perf, 0, sizeof(struct trace_perf));\n\tdefault_perf_init_pe(&perf->pe);\n\tperf->cpu = cpu;\n\tperf->pages = pages;\n\tperf->pid = pid;\n\tperf->fd = -1;\n\n\treturn 0;\n}\n\n/**\n * tcmd_perf_close - Close perf session\n *\n * @perf: structure, representing context of a running perf session, opened\n *\t  with tcmd_perf_open()\n *\n */\nvoid __hidden tcmd_perf_close(struct trace_perf *perf)\n{\n\tif (perf->fd >= 0)\n\t\tclose(perf->fd);\n\tperf->fd = -1;\n\tif (perf->mmap && perf->mmap != MAP_FAILED)\n\t\tmunmap(perf->mmap, (perf->pages + 1) * getpagesize());\n\tperf->mmap = NULL;\n}\n\n/**\n * tcmd_perf_open - Open perf session\n *\n * @perf: structure, representing perf context that will be opened. It must be\n *\t  initialized with tcmd_perf_init().\n *\n * Returns 0 on success, or -1 in case of an error. In case of success, the\n * session must be closed with tcmd_perf_close()\n */\nint __hidden tcmd_perf_open(struct trace_perf *perf)\n{\n\tperf->fd = syscall(__NR_perf_event_open, &perf->pe, perf->pid, perf->cpu, -1, 0);\n\tif (perf->fd < 0)\n\t\treturn -1;\n\tfcntl(perf->fd, F_SETFL, O_NONBLOCK);\n\n\tperf->mmap = mmap(NULL, (perf->pages + 1) * getpagesize(),\n\t\t\t  PROT_READ | PROT_WRITE, MAP_SHARED, perf->fd, 0);\n\tif (perf->mmap == MAP_FAILED)\n\t\tgoto error;\n\n\treturn 0;\n\nerror:\n\ttcmd_perf_close(perf);\n\treturn -1;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-plugin.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdlib.h>\n#include <unistd.h>\n#include <dlfcn.h>\n#include <sys/stat.h>\n#include <libgen.h>\n#include \"trace-cmd.h\"\n#include \"trace-local.h\"\n#include \"trace-cmd-local.h\"\n\n#define LOCAL_PLUGIN_DIR \".local/lib/trace-cmd/plugins/\"\n\nstruct trace_plugin_list {\n\tstruct trace_plugin_list\t*next;\n\tchar\t\t\t\t*name;\n\tvoid\t\t\t\t*handle;\n};\n\nstruct trace_plugin_context {\n\tenum tracecmd_context context;\n\tenum tracecmd_plugin_flag flags;\n\tunion {\n\t\tvoid\t\t\t\t*data;\n\t\tstruct tracecmd_input\t\t*trace_input;\n\t\tstruct tracecmd_output\t\t*trace_output;\n\t};\n};\n\n/**\n * tracecmd_plugin_context_create - Create and initialize tracecmd plugins context.\n * @context: Context of the trace-cmd command.\n * @data: Pointer to the context specific data, which will be passed to plugins.\n *\n * Returns a pointer to created tracecmd plugins context, or NULL in case memory\n * allocation fails. The returned pointer should be freed by free ().\n */\nstruct trace_plugin_context *\ntracecmd_plugin_context_create(enum tracecmd_context context, void *data)\n{\n\tstruct trace_plugin_context *trace;\n\n\ttrace = calloc(1, sizeof(struct trace_plugin_context));\n\tif (!trace)\n\t\treturn NULL;\n\ttrace->context = context;\n\ttrace->data = data;\n\treturn trace;\n}\n\n/**\n * tracecmd_plugin_set_flag - Set a flag to tracecmd plugins context.\n * @context: Context of the trace-cmd command.\n * @flag: Flag, whil will be set.\n *\n */\nvoid tracecmd_plugin_set_flag(struct trace_plugin_context *context,\n\t\t\t      enum tracecmd_plugin_flag flag)\n{\n\tif (context)\n\t\tcontext->flags |= flag;\n}\n\n/**\n * tracecmd_plugin_context_input - Get a tracecmd_input plugin context.\n * @context: Context of the trace-cmd command.\n *\n * Returns pointer to tracecmd_input, if such context is available or\n * NULL otherwise.\n */\nstruct tracecmd_input *\ntracecmd_plugin_context_input(struct trace_plugin_context *context)\n{\n\tif (!context || context->context != TRACECMD_INPUT)\n\t\treturn NULL;\n\treturn context->trace_input;\n}\n\n/**\n * tracecmd_plugin_context_output - Get a tracecmd_output plugin context\n * @context: Context of the trace-cmd command.\n *\n * Returns pointer to tracecmd_output, if such context is available or\n * NULL otherwise.\n */\nstruct tracecmd_output *\ntracecmd_plugin_context_output(struct trace_plugin_context *context)\n{\n\tif (!context || context->context != TRACECMD_OUTPUT)\n\t\treturn NULL;\n\treturn context->trace_output;\n}\n\nstatic void\nload_plugin(struct trace_plugin_context *trace, const char *path,\n\t    const char *file, void *data)\n{\n\tstruct trace_plugin_list **plugin_list = data;\n\ttracecmd_plugin_load_func func;\n\tstruct trace_plugin_list *list;\n\tconst char *alias;\n\tchar *plugin;\n\tvoid *handle;\n\tint ret;\n\n\tret = asprintf(&plugin, \"%s/%s\", path, file);\n\tif (ret < 0) {\n\t\ttracecmd_warning(\"could not allocate plugin memory\");\n\t\treturn;\n\t}\n\n\thandle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);\n\tif (!handle) {\n\t\ttracecmd_warning(\"could not load plugin '%s'\\n%s\", plugin, dlerror());\n\t\tgoto out_free;\n\t}\n\n\talias = dlsym(handle, TRACECMD_PLUGIN_ALIAS_NAME);\n\tif (!alias)\n\t\talias = file;\n\n\tfunc = dlsym(handle, TRACECMD_PLUGIN_LOADER_NAME);\n\tif (!func) {\n\t\ttracecmd_warning(\"could not find func '%s' in plugin '%s'\\n%s\",\n\t\t\t\t TRACECMD_PLUGIN_LOADER_NAME, plugin, dlerror());\n\t\tgoto out_close;\n\t}\n\n\tlist = malloc(sizeof(*list));\n\tif (!list) {\n\t\ttracecmd_warning(\"could not allocate plugin memory\");\n\t\tgoto out_close;\n\t}\n\n\tlist->next = *plugin_list;\n\tlist->handle = handle;\n\tlist->name = plugin;\n\t*plugin_list = list;\n\n\ttracecmd_info(\"registering plugin: %s\", plugin);\n\tfunc(trace);\n\treturn;\n\n out_close:\n\tdlclose(handle);\n out_free:\n\tfree(plugin);\n}\n\nstatic void\nload_plugins_dir(struct trace_plugin_context *trace, const char *suffix,\n\t\t const char *path,\n\t\t void (*load_plugin)(struct trace_plugin_context *trace,\n\t\t\t\t     const char *path,\n\t\t\t\t     const char *name,\n\t\t\t\t     void *data),\n\t\t void *data)\n{\n\tstruct dirent *dent;\n\tstruct stat st;\n\tDIR *dir;\n\tint ret;\n\n\tret = stat(path, &st);\n\tif (ret < 0)\n\t\treturn;\n\n\tif (!S_ISDIR(st.st_mode))\n\t\treturn;\n\n\tdir = opendir(path);\n\tif (!dir)\n\t\treturn;\n\n\twhile ((dent = readdir(dir))) {\n\t\tconst char *name = dent->d_name;\n\n\t\tif (strcmp(name, \".\") == 0 ||\n\t\t    strcmp(name, \"..\") == 0)\n\t\t\tcontinue;\n\n\t\t/* Only load plugins that end in suffix */\n\t\tif (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)\n\t\t\tcontinue;\n\n\t\tload_plugin(trace, path, name, data);\n\t}\n\n\tclosedir(dir);\n}\n\nstatic char *get_source_plugins_dir(void)\n{\n\tchar *p, path[PATH_MAX+1];\n\tint ret;\n\n\tret = readlink(\"/proc/self/exe\", path, PATH_MAX);\n\tif (ret > PATH_MAX || ret < 0)\n\t\treturn NULL;\n\n\tpath[ret] = 0;\n\tdirname(path);\n\tp = strrchr(path, '/');\n\tif (!p)\n\t\treturn NULL;\n\t/* Check if we are in the the source tree */\n\tif (strcmp(p, \"/tracecmd\") != 0)\n\t\treturn NULL;\n\n\tstrcpy(p, \"/lib/trace-cmd/plugins\");\n\treturn strdup(path);\n}\n\nstatic void\nload_plugins_hook(struct trace_plugin_context *trace, const char *suffix,\n\t\t  void (*load_plugin)(struct trace_plugin_context *trace,\n\t\t\t\t      const char *path,\n\t\t\t\t      const char *name,\n\t\t\t\t      void *data),\n\t\t  void *data)\n{\n\tchar *home;\n\tchar *path;\n\tchar *envdir;\n\tint ret;\n\n\tif (trace && trace->flags & TRACECMD_DISABLE_PLUGINS)\n\t\treturn;\n\n\t/*\n\t * If a system plugin directory was defined,\n\t * check that first.\n\t */\n#ifdef PLUGIN_TRACECMD_DIR\n\tif (!trace || !(trace->flags & TRACECMD_DISABLE_SYS_PLUGINS))\n\t\tload_plugins_dir(trace, suffix, PLUGIN_TRACECMD_DIR,\n\t\t\t\t load_plugin, data);\n#endif\n\n\t/*\n\t * Next let the environment-set plugin directory\n\t * override the system defaults.\n\t */\n\tenvdir = getenv(\"TRACECMD_PLUGIN_DIR\");\n\tif (envdir)\n\t\tload_plugins_dir(trace, suffix, envdir, load_plugin, data);\n\n\t/*\n\t * Now let the home directory override the environment\n\t * or system defaults.\n\t */\n\thome = getenv(\"HOME\");\n\tif (!home)\n\t\treturn;\n\n\tret = asprintf(&path, \"%s/%s\", home, LOCAL_PLUGIN_DIR);\n\tif (ret < 0) {\n\t\ttracecmd_warning(\"could not allocate plugin memory\");\n\t\treturn;\n\t}\n\n\tload_plugins_dir(trace, suffix, path, load_plugin, data);\n\n\tfree(path);\n\n\tpath = get_source_plugins_dir();\n\tif (path) {\n\t\tload_plugins_dir(trace, suffix, path, load_plugin, data);\n\t\tfree(path);\n\t}\n}\n\n/**\n * tracecmd_load_plugins - Load trace-cmd specific plugins.\n * @context: Context of the trace-cmd command, will be passed to the plugins\n *\t     at load time.\n *\n * Returns a list of loaded plugins\n */\nstruct trace_plugin_list*\ntracecmd_load_plugins(struct trace_plugin_context *trace)\n{\n\tstruct trace_plugin_list *list = NULL;\n\n\tload_plugins_hook(trace, \".so\", load_plugin, &list);\n\treturn list;\n}\n\n/**\n * tracecmd_unload_plugins - Unload trace-cmd specific plugins.\n * @plugin_list - List of plugins, previously loaded with tracecmd_load_plugins.\n * @context: Context of the trace-cmd command, will be passed to the plugins\n *\t     at unload time.\n *\n */\nvoid\ntracecmd_unload_plugins(struct trace_plugin_list *plugin_list,\n\t\t\tstruct trace_plugin_context *trace)\n{\n\ttracecmd_plugin_unload_func func;\n\tstruct trace_plugin_list *list;\n\n\twhile (plugin_list) {\n\t\tlist = plugin_list;\n\t\tplugin_list = list->next;\n\t\tfunc = dlsym(list->handle, TRACECMD_PLUGIN_UNLOADER_NAME);\n\t\tif (func)\n\t\t\tfunc(trace);\n\t\tdlclose(list->handle);\n\t\tfree(list->name);\n\t\tfree(list);\n\t}\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-rbtree.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2023 Google, Steven Rostedt <rostedt@goodmis.org>\n *\n */\n#include <stdlib.h>\n#include <stdbool.h>\n#include \"trace-local.h\"\n#include \"trace-rbtree.h\"\n\nenum {\n\tRED,\n\tBLACK,\n};\n\nvoid __hidden tcmd_rbtree_init(struct trace_rbtree *tree, trace_rbtree_cmp_fn cmp_fn,\n\t\t\t\ttrace_rbtree_search_fn search_fn)\n{\n\tmemset(tree, 0, sizeof(*tree));\n\ttree->search = search_fn;\n\ttree->cmp = cmp_fn;\n}\n\nstatic bool is_left(struct trace_rbtree_node *node)\n{\n\treturn node == node->parent->left;\n}\n\nstatic struct trace_rbtree_node **get_parent_ptr(struct trace_rbtree *tree,\n\t\t\t\t\t\t struct trace_rbtree_node *node)\n{\n\tif (!node->parent)\n\t\treturn &tree->node;\n\telse if (is_left(node))\n\t\treturn &node->parent->left;\n\telse\n\t\treturn &node->parent->right;\n}\n\nstatic void rotate_left(struct trace_rbtree *tree,\n\t\t\tstruct trace_rbtree_node *node)\n{\n\tstruct trace_rbtree_node **parent_ptr = get_parent_ptr(tree, node);\n\tstruct trace_rbtree_node *parent = node->parent;\n\tstruct trace_rbtree_node *old_right = node->right;\n\n\t*parent_ptr = old_right;\n\tnode->right = old_right->left;\n\told_right->left = node;\n\n\tif (node->right)\n\t\tnode->right->parent = node;\n\tnode->parent = old_right;\n\told_right->parent = parent;\n}\n\nstatic void rotate_right(struct trace_rbtree *tree,\n\t\t\t struct trace_rbtree_node *node)\n{\n\tstruct trace_rbtree_node **parent_ptr = get_parent_ptr(tree, node);\n\tstruct trace_rbtree_node *parent = node->parent;\n\tstruct trace_rbtree_node *old_left = node->left;\n\n\t*parent_ptr = old_left;\n\tnode->left = old_left->right;\n\told_left->right = node;\n\n\tif (node->left)\n\t\tnode->left->parent = node;\n\tnode->parent = old_left;\n\told_left->parent = parent;\n}\n\nstatic void insert_tree(struct trace_rbtree *tree,\n\t\t\tstruct trace_rbtree_node *node)\n{\n\tstruct trace_rbtree_node *next = tree->node;\n\tstruct trace_rbtree_node *last_next = NULL;\n\tbool went_left = false;\n\n\twhile (next) {\n\t\tlast_next = next;\n\t\tif (tree->cmp(next, node) > 0) {\n\t\t\tnext = next->right;\n\t\t\twent_left = false;\n\t\t} else {\n\t\t\tnext = next->left;\n\t\t\twent_left = true;\n\t\t}\n\t}\n\n\tif (!last_next) {\n\t\ttree->node = node;\n\t\treturn;\n\t}\n\n\tif (went_left)\n\t\tlast_next->left = node;\n\telse\n\t\tlast_next->right = node;\n\n\tnode->parent = last_next;\n}\n\n#if 0\nstatic int check_node(struct trace_rbtree *tree, struct trace_rbtree_node *node)\n{\n\tif (!node->parent) {\n\t\tif (tree->node != node)\n\t\t\tgoto fail;\n\t} else {\n\t\tif (!is_left(node)) {\n\t\t\tif (node->parent->right != node)\n\t\t\t\tgoto fail;\n\t\t}\n\t}\n\treturn 0;\nfail:\n\tprintf(\"FAILED ON NODE!\");\n\tbreakpoint();\n\treturn -1;\n}\n\nstatic void check_tree(struct trace_rbtree *tree)\n{\n\tstruct trace_rbtree_node *node = tree->node;\n\n\tif (node) {\n\t\tif (check_node(tree, node))\n\t\t\treturn;\n\t\twhile (node->left) {\n\t\t\tnode = node->left;\n\t\t\tif (check_node(tree, node))\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\twhile (node) {\n\t\tif (check_node(tree, node))\n\t\t\treturn;\n\t\tif (node->right) {\n\t\t\tnode = node->right;\n\t\t\tif (check_node(tree, node))\n\t\t\t\treturn;\n\t\t\twhile (node->left) {\n\t\t\t\tnode = node->left;\n\t\t\t\tif (check_node(tree, node))\n\t\t\t\t    return;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\twhile (node->parent) {\n\t\t\tif (is_left(node))\n\t\t\t\tbreak;\n\t\t\tnode = node->parent;\n\t\t\tif (check_node(tree, node))\n\t\t\t\treturn;\n\t\t}\n\t\tnode = node->parent;\n\t}\n}\n#else\nstatic inline void check_tree(struct trace_rbtree *tree) { }\n#endif\n\nint __hidden tcmd_rbtree_insert(struct trace_rbtree *tree,\n\t\t\t\t struct trace_rbtree_node *node)\n{\n\tstruct trace_rbtree_node *uncle;\n\n\tmemset(node, 0, sizeof(*node));\n\n\tinsert_tree(tree, node);\n\tnode->color = RED;\n\twhile (node && node->parent && node->parent->color == RED) {\n\t\tif (is_left(node->parent)) {\n\t\t\tuncle = node->parent->parent->right;\n\t\t\tif (uncle && uncle->color == RED) {\n\t\t\t\tnode->parent->color = BLACK;\n\t\t\t\tuncle->color = BLACK;\n\t\t\t\tnode->parent->parent->color = RED;\n\t\t\t\tnode = node->parent->parent;\n\t\t\t} else {\n\t\t\t\tif (!is_left(node)) {\n\t\t\t\t\tnode = node->parent;\n\t\t\t\t\trotate_left(tree, node);\n\t\t\t\t\tcheck_tree(tree);\n\t\t\t\t}\n\t\t\t\tnode->parent->color = BLACK;\n\t\t\t\tnode->parent->parent->color = RED;\n\t\t\t\trotate_right(tree, node->parent->parent);\n\t\t\t\tcheck_tree(tree);\n\t\t\t}\n\t\t} else {\n\t\t\tuncle = node->parent->parent->left;\n\t\t\tif (uncle && uncle->color == RED) {\n\t\t\t\tnode->parent->color = BLACK;\n\t\t\t\tuncle->color = BLACK;\n\t\t\t\tnode->parent->parent->color = RED;\n\t\t\t\tnode = node->parent->parent;\n\t\t\t} else {\n\t\t\t\tif (is_left(node)) {\n\t\t\t\t\tnode = node->parent;\n\t\t\t\t\trotate_right(tree, node);\n\t\t\t\t\tcheck_tree(tree);\n\t\t\t\t}\n\t\t\t\tnode->parent->color = BLACK;\n\t\t\t\tnode->parent->parent->color = RED;\n\t\t\t\trotate_left(tree, node->parent->parent);\n\t\t\t\tcheck_tree(tree);\n\t\t\t}\n\t\t}\n\t}\n\tcheck_tree(tree);\n\ttree->node->color = BLACK;\n\ttree->nr_nodes++;\n\treturn 0;\n}\n\nstruct trace_rbtree_node *trace_rbtree_find(struct trace_rbtree *tree, const void *data)\n{\n\tstruct trace_rbtree_node *node = tree->node;\n\tint ret;\n\n\twhile (node) {\n\t\tret = tree->search(node, data);\n\t\tif (!ret)\n\t\t\treturn node;\n\t\tif (ret > 0)\n\t\t\tnode = node->right;\n\t\telse\n\t\t\tnode = node->left;\n\t}\n\treturn NULL;\n}\n\nstatic struct trace_rbtree_node *next_node(struct trace_rbtree_node *node)\n{\n\tif (node->right) {\n\t\tnode = node->right;\n\t\twhile (node->left)\n\t\t\tnode = node->left;\n\t\treturn node;\n\t}\n\n\twhile (node->parent && !is_left(node))\n\t\tnode = node->parent;\n\n\treturn node->parent;\n}\n\nstatic void tree_fixup(struct trace_rbtree *tree, struct trace_rbtree_node *node)\n{\n\twhile (node->parent && node->color == BLACK) {\n\t\tif (is_left(node)) {\n\t\t\tstruct trace_rbtree_node *old_right = node->parent->right;\n\n\t\t\tif (old_right->color == RED) {\n\t\t\t\told_right->color = BLACK;\n\t\t\t\tnode->parent->color = RED;\n\t\t\t\trotate_left(tree, node->parent);\n\t\t\t\told_right = node->parent->right;\n\t\t\t}\n\t\t\tif (old_right->left->color == BLACK &&\n\t\t\t    old_right->right->color == BLACK) {\n\t\t\t\told_right->color = RED;\n\t\t\t\tnode = node->parent;\n\t\t\t} else {\n\t\t\t\tif (old_right->right->color == BLACK) {\n\t\t\t\t\told_right->left->color = BLACK;\n\t\t\t\t\told_right->color = RED;\n\t\t\t\t\trotate_right(tree, old_right);\n\t\t\t\t\told_right = node->parent->right;\n\t\t\t\t}\n\t\t\t\told_right->color = node->parent->color;\n\t\t\t\tnode->parent->color = BLACK;\n\t\t\t\told_right->right->color = BLACK;\n\t\t\t\trotate_left(tree, node->parent);\n\t\t\t\tnode = tree->node;\n\t\t\t}\n\t\t} else {\n\t\t\tstruct trace_rbtree_node *old_left = node->parent->left;\n\n\t\t\tif (old_left->color == RED) {\n\t\t\t\told_left->color = BLACK;\n\t\t\t\tnode->parent->color = RED;\n\t\t\t\trotate_right(tree, node->parent);\n\t\t\t\told_left = node->parent->left;\n\t\t\t}\n\t\t\tif (old_left->right->color == BLACK &&\n\t\t\t    old_left->left->color == BLACK) {\n\t\t\t\told_left->color = RED;\n\t\t\t\tnode = node->parent;\n\t\t\t} else {\n\t\t\t\tif (old_left->left->color == BLACK) {\n\t\t\t\t\told_left->right->color = BLACK;\n\t\t\t\t\told_left->color = RED;\n\t\t\t\t\trotate_left(tree, old_left);\n\t\t\t\t\told_left = node->parent->left;\n\t\t\t\t}\n\t\t\t\told_left->color = node->parent->color;\n\t\t\t\tnode->parent->color = BLACK;\n\t\t\t\told_left->left->color = BLACK;\n\t\t\t\trotate_right(tree, node->parent);\n\t\t\t\tnode = tree->node;\n\t\t\t}\n\t\t}\n\t}\n\tnode->color = BLACK;\n}\n\nvoid trace_rbtree_delete(struct trace_rbtree *tree, struct trace_rbtree_node *node)\n{\n\tstruct trace_rbtree_node *x, *y;\n\tbool do_fixup = false;\n\n\tif (!node->left && !node->right && !node->parent) {\n\t\ttree->node = NULL;\n\t\tgoto out;\n\t}\n\n\tif (!node->left || !node->right)\n\t\ty = node;\n\telse\n\t\ty = next_node(node);\n\n\tif (y->left)\n\t\tx = y->left;\n\telse\n\t\tx = y->right;\n\n\tif (x)\n\t\tx->parent = y->parent;\n\n\tif (!y->parent) {\n\t\ttree->node = x;\n\t} else {\n\t\tif (is_left(y))\n\t\t\ty->parent->left = x;\n\t\telse\n\t\t\ty->parent->right = x;\n\t}\n\n\tdo_fixup = y->color == BLACK;\n\n\tif (y != node) {\n\t\ty->color = node->color;\n\t\ty->parent = node->parent;\n\t\ty->left = node->left;\n\t\ty->right = node->right;\n\t\tif (y->left)\n\t\t\ty->left->parent = y;\n\t\tif (y->right)\n\t\t\ty->right->parent = y;\n\t\tif (!y->parent) {\n\t\t\ttree->node = y;\n\t\t} else {\n\t\t\tif (is_left(node))\n\t\t\t\ty->parent->left = y;\n\t\t\telse\n\t\t\t\ty->parent->right = y;\n\t\t}\n\t}\n\n\tif (do_fixup)\n\t\ttree_fixup(tree, x);\n\n out:\n\tnode->parent = node->left = node->right = NULL;\n\ttree->nr_nodes--;\n\tcheck_tree(tree);\n}\n\n__hidden struct trace_rbtree_node *\ntcmd_rbtree_next(struct trace_rbtree *tree, struct trace_rbtree_node *node)\n{\n\tcheck_tree(tree);\n\t/*\n\t * When either starting or the previous iteration returned a\n\t * node with a right branch, then go to the first node (if starting)\n\t * or the right node, and then return the left most node.\n\t */\n\tif (!node || node->right) {\n\t\tif (!node)\n\t\t\tnode = tree->node;\n\t\telse\n\t\t\tnode = node->right;\n\t\twhile (node && node->left)\n\t\t\tnode = node->left;\n\t\treturn node;\n\t}\n\n\t/*\n\t * If we are here, then the previous iteration returned the\n\t * left most node of the tree or the right branch. If this\n\t * is a left node, then simply return the parent. If this\n\t * is a right node, then keep going up until its a left node,\n\t * or we finished the iteration.\n\t *\n\t * If we are here and are the top node, then there is no right\n\t * node, and this is finished (return NULL).\n\t */\n\tif (!node->parent || is_left(node))\n\t\treturn node->parent;\n\n\tdo {\n\t\tnode = node->parent;\n\t} while (node->parent && !is_left(node));\n\n\treturn node->parent;\n}\n\n/*\n * Used for freeing a tree, just quickly pop off the children in\n * no particular order. This will corrupt the tree! That is,\n * do not do any inserting or deleting of this tree after calling\n * this function.\n */\nstruct trace_rbtree_node *trace_rbtree_pop_nobalance(struct trace_rbtree *tree)\n{\n\tstruct trace_rbtree_node *node = tree->node;\n\n\tif (!node)\n\t\treturn NULL;\n\n\twhile (node->left)\n\t\tnode = node->left;\n\n\twhile (node->right)\n\t\tnode = node->right;\n\n\tif (node->parent) {\n\t\tif (is_left(node))\n\t\t\tnode->parent->left = NULL;\n\t\telse\n\t\t\tnode->parent->right = NULL;\n\t} else {\n\t\ttree->node = NULL;\n\t}\n\n\treturn node;\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-recorder.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <fcntl.h>\n#include <time.h>\n#include <poll.h>\n#include <unistd.h>\n#include <errno.h>\n\n#include \"tracefs.h\"\n#include \"trace-cmd-private.h\"\n#include \"trace-cmd-local.h\"\n#include \"event-utils.h\"\n\n/* F_GETPIPE_SZ was introduced in 2.6.35, older systems don't have it */\n#ifndef F_GETPIPE_SZ\n# define F_GETPIPE_SZ\t1032 /* The Linux number for the option */\n#endif\n\n#ifndef SPLICE_F_MOVE\n# define SPLICE_F_MOVE\t\t1\n# define SPLICE_F_NONBLOCK\t2\n# define SPLICE_F_MORE\t\t4\n# define SPLICE_F_GIFT\t\t8\n#endif\n\n#define POLL_TIMEOUT_MS\t\t1000\n\nstruct tracecmd_recorder {\n\tstruct tracefs_cpu *tcpu;\n\tint\t\tfd;\n\tint\t\tfd1;\n\tint\t\tfd2;\n\tint\t\tpage_size;\n\tint\t\tsubbuf_size;\n\tint\t\tcpu;\n\tint\t\tstop;\n\tint\t\tmax;\n\tint\t\tpages;\n\tint\t\tcount;\n\tunsigned\tflags;\n};\n\nstatic int append_file(int size, int dst, int src)\n{\n\tchar buf[size];\n\tint r;\n\n\tlseek(src, 0, SEEK_SET);\n\n\t/* If there's an error, then we are pretty much screwed :-p */\n\tdo {\n\t\tr = read(src, buf, size);\n\t\tif (r < 0)\n\t\t\treturn r;\n\t\tr = write(dst, buf, r);\n\t\tif (r < 0)\n\t\t\treturn r;\n\t} while (r);\n\treturn 0;\n}\n\nvoid tracecmd_free_recorder(struct tracecmd_recorder *recorder)\n{\n\tif (!recorder)\n\t\treturn;\n\n\tif (recorder->max) {\n\t\t/* Need to put everything into fd1 */\n\t\tif (recorder->fd == recorder->fd1) {\n\t\t\tint ret;\n\t\t\t/*\n\t\t\t * Crap, the older data is in fd2, and we need\n\t\t\t * to append fd1 onto it, and then copy over to fd1\n\t\t\t */\n\t\t\tret = append_file(recorder->page_size,\n\t\t\t\t\t  recorder->fd2, recorder->fd1);\n\t\t\t/* Error on copying, then just keep fd1 */\n\t\t\tif (ret) {\n\t\t\t\tlseek(recorder->fd1, 0, SEEK_END);\n\t\t\t\tgoto close;\n\t\t\t}\n\t\t\tlseek(recorder->fd1, 0, SEEK_SET);\n\t\t\tftruncate(recorder->fd1, 0);\n\t\t}\n\t\tappend_file(recorder->page_size, recorder->fd1, recorder->fd2);\n\t}\n close:\n\ttracefs_cpu_close(recorder->tcpu);\n\n\tif (recorder->fd1 >= 0)\n\t\tclose(recorder->fd1);\n\n\tif (recorder->fd2 >= 0)\n\t\tclose(recorder->fd2);\n\n\tfree(recorder);\n}\n\nstatic int set_nonblock(struct tracecmd_recorder *recorder)\n{\n\treturn tracefs_cpu_stop(recorder->tcpu);\n}\n\nstatic struct tracecmd_recorder *\ncreate_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,\n\t\t\t   struct tracefs_instance *instance, int maxkb, int tfd)\n{\n\tstruct tracecmd_recorder *recorder;\n\tbool nonblock = false;\n\n\trecorder = malloc(sizeof(*recorder));\n\tif (!recorder)\n\t\treturn NULL;\n\n\trecorder->flags = flags;\n\n\trecorder->page_size = getpagesize();\n\tif (maxkb) {\n\t\tint kb_per_page = recorder->page_size >> 10;\n\n\t\tif (!kb_per_page)\n\t\t\tkb_per_page = 1;\n\t\trecorder->max = maxkb / kb_per_page;\n\t\t/* keep max half */\n\t\trecorder->max >>= 1;\n\t\tif (!recorder->max)\n\t\t\trecorder->max = 1;\n\t} else\n\t\trecorder->max = 0;\n\n\trecorder->count = 0;\n\trecorder->pages = 0;\n\n\t/* fd always points to what to write to */\n\trecorder->fd = fd;\n\trecorder->fd1 = fd;\n\trecorder->fd2 = fd2;\n\n\tif (recorder->flags & TRACECMD_RECORD_POLL)\n\t\tnonblock = true;\n\n\tif (tfd >= 0)\n\t\trecorder->tcpu = tracefs_cpu_alloc_fd(tfd, recorder->page_size, nonblock);\n\telse\n\t\trecorder->tcpu = tracefs_cpu_open(instance, cpu, nonblock);\n\n\tif (!recorder->tcpu)\n\t\tgoto out_free;\n\n\trecorder->subbuf_size = tracefs_cpu_read_size(recorder->tcpu);\n\treturn recorder;\n\n out_free:\n\ttracecmd_free_recorder(recorder);\n\treturn NULL;\n}\n\nstruct tracecmd_recorder *\ntracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,\n\t\t\t\t    struct tracefs_instance *instance, int maxkb)\n{\n\treturn create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb, -1);\n}\n\nstruct tracecmd_recorder *\ntracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, struct tracefs_instance *instance)\n{\n\treturn tracecmd_create_buffer_recorder_fd2(fd, -1, cpu, flags, instance, 0);\n}\n\nstatic struct tracecmd_recorder *\n__tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags,\n\t\t\t\t  struct tracefs_instance *instance, int tfd, int maxkb)\n{\n\tstruct tracecmd_recorder *recorder;\n\tint fd, fd2 = -1;\n\tchar *file2;\n\n\tfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);\n\tif (fd < 0)\n\t\treturn NULL;\n\n\tif (maxkb) {\n\t\tint len = strlen(file);\n\n\t\tfile2 = malloc(len + 3);\n\t\tif (!file2) {\n\t\t\tclose(fd);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tsprintf(file2, \"%s.1\", file);\n\n\t\tfd2 = open(file2, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);\n\t\tif (fd2 < 0) {\n\t\t\tclose(fd);\n\t\t\tunlink(file);\n\t\t\tfree(file2);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\trecorder = create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb, tfd);\n\tif (!recorder) {\n\t\tclose(fd);\n\t\tunlink(file);\n\t\tif (fd2 != -1)\n\t\t\tclose(fd2);\n\t}\n\n\tif (fd2 != -1) {\n\t\t/* Unlink file2, we need to add everything to file at the end */\n\t\tunlink(file2);\n\t\tfree(file2);\n\t}\n\n\treturn recorder;\n}\n\nstruct tracecmd_recorder *\ntracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags,\n\t\t\t\t      struct tracefs_instance *instance, int maxkb)\n{\n\tstruct tracecmd_recorder *recorder = NULL;\n\tchar *file2;\n\tint len;\n\tint fd;\n\tint fd2;\n\n\tif (!maxkb)\n\t\treturn tracecmd_create_buffer_recorder(file, cpu, flags, instance);\n\n\tlen = strlen(file);\n\tfile2 = malloc(len + 3);\n\tif (!file2)\n\t\treturn NULL;\n\n\tsprintf(file2, \"%s.1\", file);\n\n\tfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);\n\tif (fd < 0)\n\t\tgoto out;\n\n\tfd2 = open(file2, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);\n\tif (fd2 < 0)\n\t\tgoto err;\n\n\trecorder = tracecmd_create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb);\n\tif (!recorder)\n\t\tgoto err2;\n out:\n\t/* Unlink file2, we need to add everything to file at the end */\n\tunlink(file2);\n\tfree(file2);\n\n\treturn recorder;\n err2:\n\tclose(fd2);\n err:\n\tclose(fd);\n\tunlink(file);\n\tgoto out;\n}\n\nstruct tracecmd_recorder *\ntracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags,\n\t\t\t\tstruct tracefs_instance *instance)\n{\n\treturn __tracecmd_create_buffer_recorder(file, cpu, flags, instance, -1, 0);\n}\n\n/**\n * tracecmd_create_recorder_virt - Create a recorder reading tracing data\n * from the trace_fd file descriptor instead of from the local tracefs\n * @file: output filename where tracing data will be written\n * @cpu: which CPU is being traced\n * @flags: flags configuring the recorder (see TRACECMD_RECORDER_* enums)\n * @trace_fd: file descriptor from where tracing data will be read\n */\nstruct tracecmd_recorder *\ntracecmd_create_recorder_virt(const char *file, int cpu, unsigned flags,\n\t\t\t      int trace_fd, int maxkb)\n{\n\treturn __tracecmd_create_buffer_recorder(file, cpu, flags, NULL, trace_fd, maxkb);\n}\n\nstruct tracecmd_recorder *tracecmd_create_recorder_fd(int fd, int cpu, unsigned flags)\n{\n\treturn tracecmd_create_buffer_recorder_fd(fd, cpu, flags, NULL);\n}\n\nstruct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu, unsigned flags)\n{\n\treturn tracecmd_create_buffer_recorder(file, cpu, flags, NULL);\n}\n\nstruct tracecmd_recorder *\ntracecmd_create_recorder_maxkb(const char *file, int cpu, unsigned flags, int maxkb)\n{\n\treturn tracecmd_create_buffer_recorder_maxkb(file, cpu, flags, NULL, maxkb);\n}\n\nstatic inline void update_fd(struct tracecmd_recorder *recorder, int size)\n{\n\tint fd;\n\n\tif (!recorder->max)\n\t\treturn;\n\n\trecorder->count += size;\n\n\tif (recorder->count >= recorder->page_size) {\n\t\trecorder->pages += recorder->count / recorder->page_size;\n\t\trecorder->count = 0;\n\t}\n\n\tif (recorder->pages < recorder->max)\n\t\treturn;\n\n\trecorder->pages = 0;\n\n\tfd = recorder->fd;\n\n\t/* Swap fd to next file. */\n\tif (fd == recorder->fd1)\n\t\tfd = recorder->fd2;\n\telse\n\t\tfd = recorder->fd1;\n\n\t/* Zero out the new file we are writing to */\n\tlseek(fd, 0, SEEK_SET);\n\tftruncate(fd, 0);\n\n\trecorder->fd = fd;\n}\n\n/*\n * Returns -1 on error.\n *          or bytes of data read.\n */\nstatic long read_data(struct tracecmd_recorder *recorder)\n{\n\tbool nonblock = recorder->stop;\n\tchar buf[recorder->subbuf_size];\n\tlong left;\n\tlong r, w;\n\n\tr = tracefs_cpu_read(recorder->tcpu, buf, nonblock);\n\tif (r < 0)\n\t\treturn r;\n\n\tleft = r;\n\tdo {\n\t\tw = write(recorder->fd, buf + (r - left), left);\n\t\tif (w > 0) {\n\t\t\tleft -= w;\n\t\t\tupdate_fd(recorder, w);\n\t\t}\n\t} while (w >= 0 && left);\n\n\tif (w < 0)\n\t\tr = w;\n\n\treturn r;\n}\n\n/*\n * Returns -1 on error.\n *          or bytes of data read.\n */\nstatic long direct_splice_data(struct tracecmd_recorder *recorder)\n{\n\tbool nonblock = recorder->stop;\n\treturn tracefs_cpu_pipe(recorder->tcpu, recorder->fd, nonblock);\n}\n\nstatic long move_data(struct tracecmd_recorder *recorder)\n{\n\tbool nonblock = recorder->stop;\n\tlong ret;\n\n\tif (recorder->flags & TRACECMD_RECORD_NOSPLICE)\n\t\treturn read_data(recorder);\n\n\tif (recorder->flags & TRACECMD_RECORD_NOBRASS)\n\t\treturn direct_splice_data(recorder);\n\n\tret = tracefs_cpu_write(recorder->tcpu, recorder->fd, nonblock);\n\tif (ret > 0)\n\t\tupdate_fd(recorder, ret);\n\treturn ret;\n}\n\nlong tracecmd_flush_recording(struct tracecmd_recorder *recorder, bool finish)\n{\n\tchar buf[recorder->subbuf_size];\n\tlong total = 0;\n\tlong wrote = 0;\n\tlong ret;\n\n\tif (!recorder)\n\t\treturn 0;\n\n\tif (!finish)\n\t\treturn tracefs_cpu_flush_write(recorder->tcpu, recorder->fd);\n\n\tset_nonblock(recorder);\n\n\tdo {\n\t\tret = tracefs_cpu_flush_write(recorder->tcpu, recorder->fd);\n\t\tif (ret > 0)\n\t\t\twrote += ret;\n\t} while (ret > 0);\n\n\t/* Make sure we finish off with a page size boundary */\n\twrote &= recorder->subbuf_size - 1;\n\tif (wrote) {\n\t\tmemset(buf, 0, recorder->subbuf_size);\n\t\twrite(recorder->fd, buf, recorder->subbuf_size - wrote);\n\t\ttotal += recorder->subbuf_size;\n\t}\n\n\treturn total;\n}\n\nint tracecmd_start_recording(struct tracecmd_recorder *recorder, unsigned long sleep)\n{\n\tstruct timespec req = {\n\t\t.tv_sec = sleep / 1000000,\n\t\t.tv_nsec = (sleep % 1000000) * 1000,\n\t};\n\tlong read = 1;\n\tlong ret;\n\n\trecorder->stop = 0;\n\n\tdo {\n\t\t/* Only sleep if we did not read anything last time */\n\t\tif (!read && sleep)\n\t\t\tnanosleep(&req, NULL);\n\n\t\tread = 0;\n\t\tdo {\n\t\t\tret = move_data(recorder);\n\t\t\tif (ret < 0) {\n\t\t\t\tif (errno == EINTR)\n\t\t\t\t\tcontinue;\n\t\t\t\tif ((recorder->flags & TRACECMD_RECORD_POLL) &&\n\t\t\t\t    errno == EAGAIN)\n\t\t\t\t\tcontinue;\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\tread += ret;\n\t\t} while (ret > 0 && !recorder->stop);\n\t} while (!recorder->stop);\n\n\t/* Flush out the rest */\n\tret = tracecmd_flush_recording(recorder, true);\n\n\tif (ret < 0)\n\t\treturn ret;\n\n\treturn 0;\n}\n\nint tracecmd_stop_recording(struct tracecmd_recorder *recorder)\n{\n\tif (!recorder)\n\t\treturn -1;\n\n\trecorder->stop = 1;\n\n\treturn set_nonblock(recorder);\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-timesync-kvm.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2020, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com>\n *\n */\n\n#include <fcntl.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/stat.h>\n#include <dirent.h>\n#include <ctype.h>\n#include <limits.h>\n\n#include \"trace-cmd.h\"\n#include \"trace-cmd-private.h\"\n#include \"tracefs.h\"\n#include \"trace-tsync-local.h\"\n\n#define KVM_DEBUG_OFFSET_FILE\t\"tsc-offset\"\n#define KVM_DEBUG_SCALING_FILE\t\"tsc-scaling-ratio\"\n#define KVM_DEBUG_FRACTION_FILE\t\"tsc-scaling-ratio-frac-bits\"\n#define KVM_DEBUG_VCPU_DIR\t\"vcpu\"\n\n/* default KVM scaling values, taken from the Linux kernel */\n#define KVM_SCALING_AMD_DEFAULT\t\t(1ULL<<32)\n#define KVM_SCALING_INTEL_DEFAULT\t(1ULL<<48)\n\n#define KVM_SYNC_PKT_REQUEST\t1\n#define KVM_SYNC_PKT_RESPONSE\t2\n\ntypedef __s64 s64;\n\n#define KVM_ACCURACY\t0\n#define KVM_NAME\t\"kvm\"\n\nstruct kvm_clock_files {\n\tint\t\tvcpu;\n\tchar\t\t*offsets;\n\tchar\t\t*scalings;\n\tchar\t\t*frac;\n};\n\nstruct kvm_clock_sync {\n\tint\t\t\tvcpu_count;\n\tint\t\t\tmarker_fd;\n\tstruct kvm_clock_files\t*clock_files;\n\tstruct tep_handle\t*tep;\n\tint\t\t\traw_id;\n\tunsigned long long\tts;\n};\n\nstruct kvm_clock_offset_msg {\n\ts64\tts;\n\ts64\toffset;\n\ts64\tscaling;\n\ts64\tfrac;\n};\n\nstatic int read_ll_from_file(char *file, long long *res)\n{\n\tchar buf[32];\n\tint ret;\n\tint fd;\n\n\tif (!file)\n\t\treturn -1;\n\tfd = open(file, O_RDONLY | O_NONBLOCK);\n\tif (fd < 0)\n\t\treturn -1;\n\tret = read(fd, buf, 32);\n\tclose(fd);\n\tif (ret <= 0)\n\t\treturn -1;\n\n\t*res = strtoll(buf, NULL, 0);\n\n\treturn 0;\n}\n\n/*\n * Returns true if both scaling and fraction exist or both do\n * not exist. false if one exists without the other or if there\n * is a memory error.\n */\nstatic bool kvm_scaling_check_vm_cpu(char *vname, char *cpu)\n{\n\tbool has_scaling = false;\n\tbool has_frac = false;\n\tstruct stat st;\n\tchar *path;\n\tint ret;\n\n\tif (asprintf(&path, \"%s/%s/%s\", vname, cpu, KVM_DEBUG_SCALING_FILE) < 0)\n\t\treturn false;\n\tret = stat(path, &st);\n\tfree(path);\n\tif (!ret)\n\t\thas_scaling = true;\n\n\tif (asprintf(&path, \"%s/%s/%s\", vname, cpu, KVM_DEBUG_FRACTION_FILE) < 0)\n\t\treturn false;\n\tret = stat(path, &st);\n\tfree(path);\n\tif (!ret)\n\t\thas_frac = true;\n\n\tif (has_scaling != has_frac)\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic const char *kvm_debug_dir(void)\n{\n\tconst char *debugfs;\n\tstatic char *kvm_dir;\n\n\tif (kvm_dir)\n\t\treturn kvm_dir;\n\n\tdebugfs = tracefs_debug_dir();\n\tif (!debugfs)\n\t\treturn NULL;\n\n\tif (asprintf(&kvm_dir, \"%s/kvm\", debugfs) < 0)\n\t\treturn NULL;\n\n\treturn kvm_dir;\n}\n\n/*\n * Returns true if a VCPU exists with a tsc-offset file and that\n * the scaling files for ratio and fraction both exist or both\n * do not exist. False if there is no VM with a tsc-offset or\n * there is only one of the two scaling files, or there's a\n * memory issue.\n */\nstatic bool kvm_scaling_check_vm(char *name)\n{\n\tstruct dirent *entry;\n\tconst char *kvm;\n\tchar *vdir;\n\tDIR *dir;\n\tbool valid = false;\n\n\tkvm = kvm_debug_dir();\n\tif (!kvm)\n\t\treturn false;\n\n\tif (asprintf(&vdir, \"%s/%s\", kvm, name) < 0)\n\t\treturn false;\n\n\tdir = opendir(vdir);\n\tif (!dir) {\n\t\tfree(vdir);\n\t\treturn false;\n\t}\n\twhile ((entry = readdir(dir))) {\n\t\tif (entry->d_type == DT_DIR && !strncmp(entry->d_name, \"vcpu\", 4)) {\n\t\t\tif (!kvm_scaling_check_vm_cpu(vdir, entry->d_name))\n\t\t\t\tbreak;\n\t\t\tvalid = true;\n\t\t}\n\t}\n\n\tclosedir(dir);\n\tfree(vdir);\n\treturn valid && entry == NULL;\n}\n\n/*\n * Returns true if all VMs have a tsc-offset file and that\n * the scaling files for ratio and fraction both exist or both\n * do not exist. False if a VM with a tsc-offset or there is only\n * one of the two scaling files, or no VM exists or there's a memory issue.\n */\nstatic bool kvm_scaling_check(void)\n{\n\tstruct dirent *entry;\n\tconst char *kvm;\n\tDIR *dir;\n\tbool valid = false;\n\n\tkvm = kvm_debug_dir();\n\tif (!kvm)\n\t\treturn false;\n\n\tdir = opendir(kvm);\n\tif (!dir)\n\t\treturn true;\n\n\twhile ((entry = readdir(dir))) {\n\t\tif (entry->d_type == DT_DIR && isdigit(entry->d_name[0])) {\n\t\t\tif (!kvm_scaling_check_vm(entry->d_name))\n\t\t\t\tbreak;\n\t\t\tvalid = true;\n\t\t}\n\t}\n\tclosedir(dir);\n\treturn valid && entry == NULL;\n}\n\nstatic bool kvm_support_check(bool guest)\n{\n\tconst char *kvm;\n\n\t/* The kvm files are only in the host so we can ignore guests */\n\tif (guest)\n\t\treturn true;\n\n\tkvm = kvm_debug_dir();\n\tif (!kvm)\n\t\treturn false;\n\n\treturn kvm_scaling_check();\n}\n\nstatic int kvm_open_vcpu_dir(struct kvm_clock_sync *kvm, int i, char *dir_str)\n{\n\tstruct dirent *entry;\n\tchar path[PATH_MAX];\n\tDIR *dir;\n\n\tdir = opendir(dir_str);\n\tif (!dir)\n\t\tgoto error;\n\twhile ((entry = readdir(dir))) {\n\t\tif (entry->d_type != DT_DIR) {\n\t\t\tif (!strcmp(entry->d_name, KVM_DEBUG_OFFSET_FILE)) {\n\t\t\t\tsnprintf(path, sizeof(path), \"%s/%s\",\n\t\t\t\t\t dir_str, entry->d_name);\n\t\t\t\tkvm->clock_files[i].offsets = strdup(path);\n\t\t\t}\n\t\t\tif (!strcmp(entry->d_name, KVM_DEBUG_SCALING_FILE)) {\n\t\t\t\tsnprintf(path, sizeof(path), \"%s/%s\",\n\t\t\t\t\t dir_str, entry->d_name);\n\t\t\t\tkvm->clock_files[i].scalings = strdup(path);\n\t\t\t}\n\t\t\tif (!strcmp(entry->d_name, KVM_DEBUG_FRACTION_FILE)) {\n\t\t\t\tsnprintf(path, sizeof(path), \"%s/%s\",\n\t\t\t\t\t dir_str, entry->d_name);\n\t\t\t\tkvm->clock_files[i].frac = strdup(path);\n\t\t\t}\n\t\t}\n\t}\n\tif (!kvm->clock_files[i].offsets)\n\t\tgoto error;\n\tclosedir(dir);\n\treturn 0;\n\nerror:\n\tif (dir)\n\t\tclosedir(dir);\n\tfree(kvm->clock_files[i].offsets);\n\tkvm->clock_files[i].offsets = NULL;\n\tfree(kvm->clock_files[i].scalings);\n\tkvm->clock_files[i].scalings = NULL;\n\tfree(kvm->clock_files[i].frac);\n\tkvm->clock_files[i].frac = NULL;\n\treturn -1;\n}\n\nstatic int cmp_clock(const void *A, const void *B)\n{\n\tconst struct kvm_clock_files *a = A;\n\tconst struct kvm_clock_files *b = B;\n\n\tif (a->vcpu < b->vcpu)\n\t\treturn -1;\n\treturn a->vcpu > b->vcpu;\n}\n\nstatic int kvm_open_debug_files(struct kvm_clock_sync *kvm, int pid)\n{\n\tchar *vm_dir_str = NULL;\n\tstruct dirent *entry;\n\tchar *pid_str = NULL;\n\tchar path[PATH_MAX];\n\tlong vcpu;\n\tDIR *dir;\n\tint i;\n\n\tdir = opendir(kvm_debug_dir());\n\tif (!dir)\n\t\tgoto error;\n\tif (asprintf(&pid_str, \"%d-\", pid) <= 0)\n\t\tgoto error;\n\twhile ((entry = readdir(dir))) {\n\t\tif (!(entry->d_type == DT_DIR &&\n\t\t    !strncmp(entry->d_name, pid_str, strlen(pid_str))))\n\t\t\tcontinue;\n\t\tasprintf(&vm_dir_str, \"%s/%s\", kvm_debug_dir(), entry->d_name);\n\t\tbreak;\n\t}\n\tclosedir(dir);\n\tdir = NULL;\n\tif (!vm_dir_str)\n\t\tgoto error;\n\tdir = opendir(vm_dir_str);\n\tif (!dir)\n\t\tgoto error;\n\ti = 0;\n\twhile ((entry = readdir(dir))) {\n\t\tif (!(entry->d_type == DT_DIR &&\n\t\t    !strncmp(entry->d_name, KVM_DEBUG_VCPU_DIR, strlen(KVM_DEBUG_VCPU_DIR))))\n\t\t\tcontinue;\n\t\tif (i == kvm->vcpu_count)\n\t\t\tgoto error;\n\t\tvcpu = strtol(entry->d_name + strlen(KVM_DEBUG_VCPU_DIR), NULL, 10);\n\t\tkvm->clock_files[i].vcpu = vcpu;\n\t\tsnprintf(path, sizeof(path), \"%s/%s\", vm_dir_str, entry->d_name);\n\t\tif (kvm_open_vcpu_dir(kvm, i, path) < 0)\n\t\t\tgoto error;\n\t\ti++;\n\t}\n\tif (i < kvm->vcpu_count)\n\t\tgoto error;\n\n\tqsort(kvm->clock_files, kvm->vcpu_count, sizeof(*kvm->clock_files), cmp_clock);\n\n\tclosedir(dir);\n\tfree(pid_str);\n\tfree(vm_dir_str);\n\treturn 0;\nerror:\n\tfree(pid_str);\n\tfree(vm_dir_str);\n\tif (dir)\n\t\tclosedir(dir);\n\treturn -1;\n}\n\nstatic int kvm_clock_sync_init_host(struct tracecmd_time_sync *tsync,\n\t\t\t\t    struct kvm_clock_sync *kvm)\n{\n\tkvm->vcpu_count = tsync->vcpu_count;\n\tkvm->clock_files = calloc(kvm->vcpu_count, sizeof(*kvm->clock_files));\n\tif (!kvm->clock_files)\n\t\tgoto error;\n\tif (kvm_open_debug_files(kvm, tsync->guest_pid) < 0)\n\t\tgoto error;\n\treturn 0;\n\nerror:\n\tfree(kvm->clock_files);\n\treturn -1;\n}\n\nstatic int kvm_clock_sync_init_guest(struct tracecmd_time_sync *tsync,\n\t\t\t\t     struct kvm_clock_sync *kvm)\n{\n\tconst char *systems[] = {\"ftrace\", NULL};\n\tstruct clock_sync_context *clock_context;\n\tstruct tep_event *raw;\n\tchar *path;\n\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\tpath = tracefs_instance_get_dir(clock_context->instance);\n\tif (!path)\n\t\tgoto error;\n\tkvm->tep = tracefs_local_events_system(path, systems);\n\ttracefs_put_tracing_file(path);\n\tif (!kvm->tep)\n\t\tgoto error;\n\traw = tep_find_event_by_name(kvm->tep, \"ftrace\", \"raw_data\");\n\tif (!raw)\n\t\tgoto error;\n\n\tkvm->raw_id = raw->id;\n\ttep_set_file_bigendian(kvm->tep, tracecmd_host_bigendian());\n\ttep_set_local_bigendian(kvm->tep, tracecmd_host_bigendian());\n\n\tpath = tracefs_instance_get_file(clock_context->instance, \"trace_marker_raw\");\n\tif (!path)\n\t\tgoto error;\n\tkvm->marker_fd = open(path, O_WRONLY);\n\ttracefs_put_tracing_file(path);\n\n\treturn 0;\n\nerror:\n\tif (kvm->tep)\n\t\ttep_free(kvm->tep);\n\tif (kvm->marker_fd >= 0)\n\t\tclose(kvm->marker_fd);\n\n\treturn -1;\n}\n\nstatic int kvm_clock_sync_init(struct tracecmd_time_sync *tsync)\n{\n\tstruct clock_sync_context *clock_context;\n\tstruct kvm_clock_sync *kvm;\n\tint ret;\n\n\tif (!tsync || !tsync->context)\n\t\treturn -1;\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\n\tif (!kvm_support_check(clock_context->is_guest))\n\t\treturn -1;\n\tkvm = calloc(1, sizeof(struct kvm_clock_sync));\n\tif (!kvm)\n\t\treturn -1;\n\tkvm->marker_fd = -1;\n\tif (clock_context->is_guest)\n\t\tret = kvm_clock_sync_init_guest(tsync, kvm);\n\telse\n\t\tret = kvm_clock_sync_init_host(tsync, kvm);\n\tif (ret < 0)\n\t\tgoto error;\n\n\tclock_context->proto_data = kvm;\n\treturn 0;\n\nerror:\n\tfree(kvm);\n\treturn -1;\n}\n\nstatic int kvm_clock_sync_free(struct tracecmd_time_sync *tsync)\n{\n\tstruct clock_sync_context *clock_context;\n\tstruct kvm_clock_sync *kvm = NULL;\n\tint i;\n\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\tif (clock_context)\n\t\tkvm = (struct kvm_clock_sync *)clock_context->proto_data;\n\tif (kvm) {\n\t\tfor (i = 0; i < kvm->vcpu_count; i++) {\n\t\t\tfree(kvm->clock_files[i].offsets);\n\t\t\tfree(kvm->clock_files[i].scalings);\n\t\t\tfree(kvm->clock_files[i].frac);\n\t\t}\n\t\tif (kvm->tep)\n\t\t\ttep_free(kvm->tep);\n\t\tif (kvm->marker_fd >= 0)\n\t\t\tclose(kvm->marker_fd);\n\t\tfree(kvm);\n\t}\n\treturn -1;\n}\n\nstatic int kvm_clock_host(struct tracecmd_time_sync *tsync,\n\t\t\t  long long *offset, long long *scaling, long long *frac,\n\t\t\t  long long *timestamp, unsigned int cpu)\n{\n\tchar sync_proto[TRACECMD_TSYNC_PNAME_LENGTH];\n\tstruct clock_sync_context *clock_context;\n\tstruct kvm_clock_offset_msg packet;\n\tstruct kvm_clock_sync *kvm = NULL;\n\tlong long kvm_scaling = 1;\n\tunsigned int sync_msg;\n\tlong long kvm_offset;\n\tlong long kvm_frac = 0;\n\tunsigned int size;\n\tchar *msg;\n\tint ret;\n\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\tif (clock_context)\n\t\tkvm = (struct kvm_clock_sync *)clock_context->proto_data;\n\tif (!kvm || !kvm->clock_files || !kvm->clock_files[0].offsets)\n\t\treturn -1;\n\tif (cpu >= kvm->vcpu_count)\n\t\treturn -1;\n\tret = read_ll_from_file(kvm->clock_files[cpu].offsets, &kvm_offset);\n\tif (ret < 0)\n\t\treturn -1;\n\n\tif (kvm->clock_files[cpu].scalings) {\n\t\tread_ll_from_file(kvm->clock_files[cpu].scalings, &kvm_scaling);\n\t\tif (kvm_scaling == KVM_SCALING_AMD_DEFAULT ||\n\t\t    kvm_scaling == KVM_SCALING_INTEL_DEFAULT)\n\t\t\tkvm_scaling = 1;\n\t}\n\n\tif (kvm->clock_files[cpu].frac && kvm_scaling != 1)\n\t\tret = read_ll_from_file(kvm->clock_files[cpu].frac, &kvm_frac);\n\tmsg = (char *)&packet;\n\tsize = sizeof(packet);\n\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t  sync_proto, &sync_msg,\n\t\t\t\t\t  &size, &msg);\n\tif (ret || strncmp(sync_proto, KVM_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t    sync_msg != KVM_SYNC_PKT_REQUEST)\n\t\treturn -1;\n\n\tpacket.offset = -kvm_offset;\n\tpacket.scaling = kvm_scaling;\n\tpacket.frac = kvm_frac;\n\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, KVM_NAME,\n\t\t\t\t\t  KVM_SYNC_PKT_RESPONSE, sizeof(packet),\n\t\t\t\t\t  (char *)&packet);\n\tif (ret)\n\t\treturn -1;\n\n\t*scaling = packet.scaling;\n\t*offset = packet.offset;\n\t*frac = kvm_frac;\n\t*timestamp = packet.ts;\n\n\treturn 0;\n}\n\n#define KVM_EVENT_MARKER\t\"kvm sync event\"\nstatic int kvm_marker_find(struct tep_event *event, struct tep_record *record,\n\t\t\t   int cpu, void *context)\n{\n\tstruct kvm_clock_sync *kvm = (struct kvm_clock_sync *)context;\n\tstruct tep_format_field *field;\n\tstruct tep_format_field *id;\n\tchar *marker;\n\n\t/* Make sure this is our event */\n\tif (event->id != kvm->raw_id)\n\t\treturn 0;\n\tid = tep_find_field(event, \"id\");\n\tfield = tep_find_field(event, \"buf\");\n\tif (field && id &&\n\t    record->size >= (id->offset + strlen(KVM_EVENT_MARKER) + 1)) {\n\t\tmarker = (char *)(record->data + id->offset);\n\t\tif (!strcmp(marker, KVM_EVENT_MARKER)) {\n\t\t\tkvm->ts = record->ts;\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nstatic int kvm_clock_guest(struct tracecmd_time_sync *tsync,\n\t\t\t   long long *offset,\n\t\t\t   long long *scaling,\n\t\t\t   long long *frac,\n\t\t\t   long long *timestamp)\n{\n\tchar sync_proto[TRACECMD_TSYNC_PNAME_LENGTH];\n\tstruct clock_sync_context *clock_context;\n\tstruct kvm_clock_offset_msg packet;\n\tstruct kvm_clock_sync *kvm = NULL;\n\tunsigned int sync_msg;\n\tunsigned int size;\n\tchar *msg;\n\tint ret;\n\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\tif (clock_context)\n\t\tkvm = (struct kvm_clock_sync *)clock_context->proto_data;\n\tif (!kvm)\n\t\treturn -1;\n\tkvm->ts = 0;\n\tmemset(&packet, 0, sizeof(packet));\n\ttracefs_instance_file_write(clock_context->instance, \"trace\", \"\\0\");\n\twrite(kvm->marker_fd, KVM_EVENT_MARKER, strlen(KVM_EVENT_MARKER) + 1);\n\tkvm->ts = 0;\n\ttracefs_iterate_raw_events(kvm->tep, clock_context->instance,\n\t\t\t\t   NULL, 0, kvm_marker_find, kvm);\n\tpacket.ts = kvm->ts;\n\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, KVM_NAME,\n\t\t\t\t\t  KVM_SYNC_PKT_REQUEST, sizeof(packet),\n\t\t\t\t\t  (char *)&packet);\n\tif (ret)\n\t\treturn -1;\n\tmsg = (char *)&packet;\n\tsize = sizeof(packet);\n\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t  sync_proto, &sync_msg,\n\t\t\t\t\t  &size, &msg);\n\tif (ret || strncmp(sync_proto, KVM_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t    sync_msg != KVM_SYNC_PKT_RESPONSE)\n\t\treturn -1;\n\n\t*scaling = packet.scaling;\n\t*offset = packet.offset;\n\t*frac = packet.frac;\n\t*timestamp = packet.ts;\n\treturn 0;\n}\n\nstatic int kvm_clock_sync_calc(struct tracecmd_time_sync *tsync,\n\t\t\t       long long *offset, long long *scaling, long long *frac,\n\t\t\t       long long *timestamp, unsigned int cpu)\n{\n\tstruct clock_sync_context *clock_context;\n\tint ret;\n\n\tif (!tsync || !tsync->context)\n\t\treturn -1;\n\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\n\tif (clock_context->is_guest)\n\t\tret = kvm_clock_guest(tsync, offset, scaling, frac, timestamp);\n\telse\n\t\tret = kvm_clock_host(tsync, offset, scaling, frac, timestamp, cpu);\n\treturn ret;\n}\n\nint kvm_clock_sync_register(void)\n{\n\tint role = TRACECMD_TIME_SYNC_ROLE_GUEST;\n\tint clock = 0;\n\n\tif (kvm_support_check(false)) {\n\t\trole |= TRACECMD_TIME_SYNC_ROLE_HOST;\n\t\tclock = TRACECMD_CLOCK_X86_TSC;\n\t}\n\treturn tracecmd_tsync_proto_register(KVM_NAME, KVM_ACCURACY,\n\t\t\t\t\t     role, clock, 0,\n\t\t\t\t\t     kvm_clock_sync_init,\n\t\t\t\t\t     kvm_clock_sync_free,\n\t\t\t\t\t     kvm_clock_sync_calc);\n}\n\nint kvm_clock_sync_unregister(void)\n{\n\treturn tracecmd_tsync_proto_unregister(KVM_NAME);\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-timesync-ptp.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2019, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com>\n *\n */\n\n#include <fcntl.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <arpa/inet.h>\n#include <sys/types.h>\n#include <linux/types.h>\n#include <time.h>\n#include <sched.h>\n#include <limits.h>\n\n#include \"trace-cmd.h\"\n#include \"trace-cmd-private.h\"\n#include \"tracefs.h\"\n#include \"trace-tsync-local.h\"\n#include \"trace-msg.h\"\n#include \"trace-cmd-local.h\"\n\ntypedef __be32 be32;\ntypedef __u64 u64;\ntypedef __s64 s64;\n\n#define PTP_SYNC_LOOP\t339\n\n#define PTP_SYNC_PKT_START\t1\n#define PTP_SYNC_PKT_PROBE\t2\n#define PTP_SYNC_PKT_PROBES\t3\n#define PTP_SYNC_PKT_OFFSET\t4\n#define PTP_SYNC_PKT_END\t5\n\n/* print time sync debug messages */\n/* #define TSYNC_DEBUG */\n\nstruct ptp_clock_sync {\n\tstruct tep_handle\t*tep;\n\tstruct tep_format_field\t*id;\n\tint\t\t\traw_id;\n\tint\t\t\tmarker_fd;\n\tint\t\t\tseries_id;\n\tint\t\t\tflags;\n\tint\t\t\tdebug_fd;\n};\n\nenum {\n/*\n * Consider only the probe with fastest response time,\n * otherwise make a histogram from all probes.\n */\n\tPTP_FLAG_FASTEST_RESPONSE\t\t= (1 << 0),\n/*\n * Use trace marker to get the clock,\n * otherwise use the system clock directly.\n */\n\tPTP_FLAG_USE_MARKER\t\t\t= (1 << 1),\n};\nstatic int ptp_flags = PTP_FLAG_FASTEST_RESPONSE | PTP_FLAG_USE_MARKER;\n\n/*\n * Calculated using formula [CPU rate]*[calculated offset deviation]\n * tested on 3GHz CPU, with x86-tsc trace clock and compare the calculated\n * offset with /sys/kernel/debug/kvm/<VM ID>/vcpu0/tsc-offset\n * measured 2000ns deviation\n * using PTP flags PTP_FLAG_FASTEST_RESPONSE | PTP_FLAG_USE_MARKER\n */\n#define PTP_ACCURACY\t6000\n#define PTP_NAME\t\"ptp\"\n\nstruct ptp_clock_start_msg {\n\tbe32\tseries_id;\n\tbe32\tflags;\n} __packed;\n\nstruct ptp_clock_sample {\n\ts64\t\tts;\n\tbe32\t\tid;\n} __packed;\n\nstruct ptp_clock_result_msg {\n\tbe32\t\t\tseries_id;\n\tbe32\t\t\tcount;\n\tstruct ptp_clock_sample\tsamples[2*PTP_SYNC_LOOP];\n} __packed;\n\nstruct ptp_clock_offset_msg {\n\ts64\tts;\n\ts64\toffset;\n};\n\nstruct ptp_markers_context {\n\tstruct clock_sync_context\t*clock;\n\tstruct ptp_clock_sync\t\t*ptp;\n\tstruct ptp_clock_result_msg\tmsg;\n\tint\t\t\t\tsize;\n};\n\nstruct ptp_marker_buf {\n\tint local_id;\n\tint remote_id;\n\tint count;\n\tint packet_id;\n} __packed;\n\nstruct ptp_marker {\n\tint series_id;\n\tstruct ptp_marker_buf data;\n} __packed;\n\nstatic int ptp_clock_sync_init(struct tracecmd_time_sync *tsync)\n{\n\tconst char *systems[] = {\"ftrace\", NULL};\n\tstruct clock_sync_context *clock_context;\n\tstruct ptp_clock_sync *ptp;\n\tstruct tep_event *raw;\n\tchar *path;\n\n\tif (!tsync || !tsync->context)\n\t\treturn -1;\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\tif (clock_context->proto_data)\n\t\treturn 0;\n\n\tptp = calloc(1, sizeof(struct ptp_clock_sync));\n\tif (!ptp)\n\t\treturn -1;\n\n\tptp->marker_fd = -1;\n\tptp->debug_fd = -1;\n\n\tpath = tracefs_instance_get_dir(clock_context->instance);\n\tif (!path)\n\t\tgoto error;\n\tptp->tep = tracefs_local_events_system(path, systems);\n\ttracefs_put_tracing_file(path);\n\tif (!ptp->tep)\n\t\tgoto error;\n\traw = tep_find_event_by_name(ptp->tep, \"ftrace\", \"raw_data\");\n\tif (!raw)\n\t\tgoto error;\n\tptp->id = tep_find_field(raw, \"id\");\n\tif (!ptp->id)\n\t\tgoto error;\n\tptp->raw_id = raw->id;\n\n\ttep_set_file_bigendian(ptp->tep, tracecmd_host_bigendian());\n\ttep_set_local_bigendian(ptp->tep, tracecmd_host_bigendian());\n\n\tpath = tracefs_instance_get_file(clock_context->instance, \"trace_marker_raw\");\n\tif (!path)\n\t\tgoto error;\n\tptp->marker_fd = open(path, O_WRONLY);\n\ttracefs_put_tracing_file(path);\n\n\tclock_context->proto_data = ptp;\n\n#ifdef TSYNC_DEBUG\n\tif (clock_context->is_server) {\n\t\tchar buff[256];\n\t\tint res_fd;\n\n\t\tsprintf(buff, \"res-id%d.txt\", clock_context->remote_id);\n\n\t\tres_fd = open(buff, O_CREAT|O_WRONLY|O_TRUNC, 0644);\n\t\tif (res_fd > 0)\n\t\t\tclose(res_fd);\n\t}\n#endif\n\n\treturn 0;\n\nerror:\n\tif (ptp) {\n\t\ttep_free(ptp->tep);\n\t\tif (ptp->marker_fd >= 0)\n\t\t\tclose(ptp->marker_fd);\n\t}\n\tfree(ptp);\n\treturn -1;\n}\n\nstatic int ptp_clock_sync_free(struct tracecmd_time_sync *tsync)\n{\n\tstruct clock_sync_context *clock_context;\n\tstruct ptp_clock_sync *ptp;\n\n\tif (!tsync || !tsync->context)\n\t\treturn -1;\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\n\tif (clock_context && clock_context->proto_data) {\n\t\tptp = (struct ptp_clock_sync *)clock_context->proto_data;\n\t\ttep_free(ptp->tep);\n\t\tif (ptp->marker_fd >= 0)\n\t\t\tclose(ptp->marker_fd);\n\t\tif (ptp->debug_fd >= 0)\n\t\t\tclose(ptp->debug_fd);\n\t\tfree(clock_context->proto_data);\n\t\tclock_context->proto_data = NULL;\n\t}\n\treturn 0;\n}\n\n/* Save the timestamps of sent ('s') and returned ('r') probes in the\n * ctx->msg.samples[] array. Depending of the context (server or client), there\n * may be only returned probes, or both sent and returned probes. The returned\n * probes are saved first in the array, after them are the sent probes.\n * Depending of the context, the array can be with size:\n *  [0 .. max data.count] - holds only returned probes\n *  [0 .. 2 * max data.count] - holds both returned and sent probes\n */\nstatic void ptp_probe_store(struct ptp_markers_context *ctx,\n\t\t\t    struct ptp_marker *marker,\n\t\t\t    unsigned long long ts)\n{\n\tint index = -1;\n\n\tif (marker->data.packet_id == 'r' &&\n\t    marker->data.count <= ctx->size) {\n\t\tindex = marker->data.count - 1;\n\t} else if (marker->data.packet_id == 's' &&\n\t\t  marker->data.count * 2 <= ctx->size){\n\t\tindex = ctx->size / 2 + marker->data.count - 1;\n\t}\n\n\tif (index >= 0) {\n\t\tctx->msg.samples[index].id = marker->data.count;\n\t\tctx->msg.samples[index].ts = ts;\n\t\tctx->msg.count++;\n\t}\n}\n\nstatic int ptp_marker_find(struct tep_event *event, struct tep_record *record,\n\t\t\t   int cpu, void *context)\n{\n\tstruct ptp_markers_context *ctx;\n\tstruct ptp_marker *marker;\n\n\tctx = (struct ptp_markers_context *)context;\n\n\t/* Make sure this is our event */\n\tif (event->id != ctx->ptp->raw_id || !ctx->ptp->id)\n\t\treturn 0;\n\tif (record->size >= (ctx->ptp->id->offset + sizeof(struct ptp_marker))) {\n\t\tmarker = (struct ptp_marker *)(record->data + ctx->ptp->id->offset);\n\t\tif (marker->data.local_id == ctx->clock->local_id &&\n\t\t    marker->data.remote_id == ctx->clock->remote_id &&\n\t\t    marker->series_id == ctx->ptp->series_id &&\n\t\t    marker->data.count)\n\t\t\tptp_probe_store(ctx, marker, record->ts);\n\t}\n\n\treturn 0;\n}\n\nstatic inline bool good_probe(struct ptp_clock_sample *server_sample,\n\t\t\t      struct ptp_clock_sample *send_sample,\n\t\t\t      struct ptp_clock_sample *client_sample,\n\t\t\t      int *bad_probes)\n{\n\tif (server_sample->ts && send_sample->ts && client_sample->ts &&\n\t    server_sample->id == send_sample->id &&\n\t    server_sample->id == client_sample->id)\n\t\treturn true;\n\t(*bad_probes)++;\n\treturn false;\n}\n\nstatic int ptp_calc_offset_fastest(struct clock_sync_context *clock,\n\t\t\t   struct ptp_clock_result_msg *server,\n\t\t\t   struct ptp_clock_result_msg *client,\n\t\t\t   long long *offset_ret, long long *ts_ret,\n\t\t\t   int *bad_probes)\n{\n\tstruct ptp_clock_sample *sample_send;\n\tlong long delta_min = LLONG_MAX;\n\tlong long offset = 0;\n\tlong long delta = 0;\n\tlong long ts = 0;\n\tint max_i;\n\tint i;\n\n\t*bad_probes = 0;\n\tsample_send = server->samples + (server->count / 2);\n\tmax_i = server->count / 2 < client->count ?\n\t\tserver->count / 2 : client->count;\n\tfor (i = 0; i < max_i; i++) {\n\t\tif (!good_probe(&server->samples[i], &sample_send[i],\n\t\t    &client->samples[i], bad_probes))\n\t\t\tcontinue;\n\t\tts = (sample_send[i].ts + server->samples[i].ts) / 2;\n\t\toffset = client->samples[i].ts - ts;\n\n\t\tdelta = server->samples[i].ts - sample_send[i].ts;\n\t\tif (delta_min > delta) {\n\t\t\tdelta_min = delta;\n\t\t\t*offset_ret = offset;\n\t\t\t*ts_ret = ts;\n\t\t}\n#ifdef TSYNC_DEBUG\n\t\t{\n\t\t\tstruct ptp_clock_sync *ptp;\n\n\t\t\tptp = (struct ptp_clock_sync *)clock->proto_data;\n\t\t\tif (ptp && ptp->debug_fd > 0) {\n\t\t\t\tchar buff[256];\n\n\t\t\t\tsprintf(buff, \"%lld %lld %lld\\n\",\n\t\t\t\t\tts, client->samples[i].ts, offset);\n\t\t\t\twrite(ptp->debug_fd, buff, strlen(buff));\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\treturn 0;\n}\n\nstatic int ptp_calc_offset_hist(struct clock_sync_context *clock,\n\t\t\t   struct ptp_clock_result_msg *server,\n\t\t\t   struct ptp_clock_result_msg *client,\n\t\t\t   long long *offset_ret, long long *ts_ret,\n\t\t\t   int *bad_probes)\n{\n\tstruct ptp_clock_sample *sample_send;\n\tlong long timestamps[PTP_SYNC_LOOP];\n\tlong long offsets[PTP_SYNC_LOOP];\n\tlong long offset_min = LLONG_MAX;\n\tlong long offset_max = 0;\n\tint hist[PTP_SYNC_LOOP];\n\tint ind, max = 0;\n\tlong long bin;\n\tint i, k = 0;\n\n\t*bad_probes = 0;\n\tmemset(hist, 0, sizeof(int) * PTP_SYNC_LOOP);\n\tsample_send = server->samples + (server->count / 2);\n\tfor (i = 0; i * 2 < server->count && i < client->count; i++) {\n\t\tif (!good_probe(&server->samples[i], &sample_send[i],\n\t\t    &client->samples[i], bad_probes))\n\t\t\tcontinue;\n\t\ttimestamps[k] = (sample_send[i].ts + server->samples[i].ts) / 2;\n\t\toffsets[k] = client->samples[i].ts - timestamps[k];\n\t\tif (offset_max < llabs(offsets[k]))\n\t\t\toffset_max = llabs(offsets[k]);\n\t\tif (offset_min > llabs(offsets[k]))\n\t\t\toffset_min = llabs(offsets[k]);\n#ifdef TSYNC_DEBUG\n\t\t{\n\t\t\tstruct ptp_clock_sync *ptp;\n\n\t\t\tptp = (struct ptp_clock_sync *)clock->proto_data;\n\n\t\t\tif (ptp && ptp->debug_fd > 0) {\n\t\t\t\tchar buff[256];\n\n\t\t\t\tsprintf(buff, \"%lld %lld %lld\\n\",\n\t\t\t\t\ttimestamps[k],\n\t\t\t\t\tclient->samples[i].ts, offsets[k]);\n\t\t\t\twrite(ptp->debug_fd, buff, strlen(buff));\n\t\t\t}\n\t\t}\n#endif\n\t\tk++;\n\t}\n\n\tbin = (offset_max - offset_min) / PTP_SYNC_LOOP;\n\tfor (i = 0; i < k; i++) {\n\t\tind = (llabs(offsets[i]) - offset_min) / bin;\n\t\tif (ind < PTP_SYNC_LOOP) {\n\t\t\thist[ind]++;\n\t\t\tif (max < hist[ind]) {\n\t\t\t\tmax = hist[ind];\n\t\t\t\t*offset_ret = offsets[i];\n\t\t\t\t*ts_ret = timestamps[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nstatic void ntoh_ptp_results(struct ptp_clock_result_msg *msg)\n{\n\tint i;\n\n\tmsg->count = ntohl(msg->count);\n\tfor (i = 0; i < msg->count; i++) {\n\t\tmsg->samples[i].id = ntohl(msg->samples[i].id);\n\t\tmsg->samples[i].ts = ntohll(msg->samples[i].ts);\n\t}\n\tmsg->series_id = ntohl(msg->series_id);\n}\n\n\nstatic void hton_ptp_results(struct ptp_clock_result_msg *msg)\n{\n\tint i;\n\n\tfor (i = 0; i < msg->count; i++) {\n\t\tmsg->samples[i].id = htonl(msg->samples[i].id);\n\t\tmsg->samples[i].ts = htonll(msg->samples[i].ts);\n\t}\n\tmsg->series_id = htonl(msg->series_id);\n\tmsg->count = htonl(msg->count);\n}\n\nstatic inline void ptp_track_clock(struct ptp_markers_context *ctx,\n\t\t\t\t   struct ptp_marker *marker)\n{\n\tif (ctx->ptp->flags & PTP_FLAG_USE_MARKER) {\n\t\twrite(ctx->ptp->marker_fd, marker, sizeof(struct ptp_marker));\n\t} else {\n\t\tstruct timespec clock;\n\t\tunsigned long long ts;\n\n\t\tclock_gettime(CLOCK_MONOTONIC_RAW, &clock);\n\t\tts = clock.tv_sec * 1000000000LL;\n\t\tts += clock.tv_nsec;\n\t\tptp_probe_store(ctx, marker, ts);\n\t}\n}\n\nstatic int ptp_clock_client(struct tracecmd_time_sync *tsync,\n\t\t\t    long long *offset, long long *timestamp)\n{\n\tchar sync_proto[TRACECMD_TSYNC_PNAME_LENGTH];\n\tstruct clock_sync_context *clock_context;\n\tstruct ptp_clock_offset_msg res_offset;\n\tstruct ptp_clock_start_msg start;\n\tstruct ptp_markers_context ctx;\n\tstruct ptp_clock_sync *ptp;\n\tstruct ptp_marker marker;\n\tunsigned int sync_msg;\n\tunsigned int size;\n\tchar *msg;\n\tint count;\n\tint ret;\n\n\tif (!tsync || !tsync->context || !tsync->msg_handle)\n\t\treturn -1;\n\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\tif (clock_context->proto_data == NULL)\n\t\treturn -1;\n\n\tptp = (struct ptp_clock_sync *)clock_context->proto_data;\n\tsize = sizeof(start);\n\tmsg = (char *)&start;\n\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t  sync_proto, &sync_msg,\n\t\t\t\t\t  &size, &msg);\n\tif (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t    sync_msg != PTP_SYNC_PKT_START)\n\t\treturn -1;\n\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME,\n\t\t\t\t\t  PTP_SYNC_PKT_START, sizeof(start),\n\t\t\t\t\t  (char *)&start);\n\tmarker.data.local_id = clock_context->local_id;\n\tmarker.data.remote_id = clock_context->remote_id;\n\tmarker.series_id = ntohl(start.series_id);\n\tmarker.data.packet_id = 'r';\n\tptp->series_id = marker.series_id;\n\tptp->flags = ntohl(start.flags);\n\tmsg = (char *)&count;\n\tsize = sizeof(count);\n\tctx.msg.count = 0;\n\tctx.size = PTP_SYNC_LOOP;\n\tctx.ptp = ptp;\n\tctx.clock = clock_context;\n\tctx.msg.series_id = ptp->series_id;\n\twhile (true) {\n\t\tcount = 0;\n\t\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t\t  sync_proto, &sync_msg,\n\t\t\t\t\t\t  &size, &msg);\n\t\tif (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t\t    sync_msg != PTP_SYNC_PKT_PROBE || !ntohl(count))\n\t\t\tbreak;\n\t\tmarker.data.count = ntohl(count);\n\t\tptp_track_clock(&ctx, &marker);\n\t\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME,\n\t\t\t\t\t\t  PTP_SYNC_PKT_PROBE,\n\t\t\t\t\t\t  sizeof(count), (char *)&count);\n\t\tif (ret)\n\t\t\tbreak;\n\t}\n\n\tif (strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t    sync_msg != PTP_SYNC_PKT_END)\n\t\treturn -1;\n\n\tif (ptp->flags & PTP_FLAG_USE_MARKER)\n\t\ttracefs_iterate_raw_events(ptp->tep, clock_context->instance,\n\t\t\t\t\t   NULL, 0, ptp_marker_find, &ctx);\n\n\thton_ptp_results(&ctx.msg);\n\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME,\n\t\t\t\t\t  PTP_SYNC_PKT_PROBES,\n\t\t\t\t\t  sizeof(ctx.msg), (char *)&ctx.msg);\n\n\tmsg = (char *)&res_offset;\n\tsize = sizeof(res_offset);\n\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t  sync_proto, &sync_msg,\n\t\t\t\t\t  &size, (char **)&msg);\n\tif (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t    sync_msg != PTP_SYNC_PKT_OFFSET)\n\t\treturn -1;\n\n\t*offset = ntohll(res_offset.offset);\n\t*timestamp = ntohll(res_offset.ts);\n\n\treturn 0;\n}\n\n\nstatic int ptp_clock_server(struct tracecmd_time_sync *tsync,\n\t\t\t    long long *offset, long long *timestamp)\n{\n\tchar sync_proto[TRACECMD_TSYNC_PNAME_LENGTH];\n\tstruct ptp_clock_result_msg *results = NULL;\n\tstruct clock_sync_context *clock_context;\n\tstruct ptp_clock_offset_msg res_offset;\n\tstruct ptp_clock_start_msg start;\n\tstruct ptp_markers_context ctx;\n\tint sync_loop = PTP_SYNC_LOOP;\n\tstruct ptp_clock_sync *ptp;\n\tstruct ptp_marker marker;\n\tunsigned int sync_msg;\n\tunsigned int size;\n\tint bad_probes;\n\tint count = 1;\n\tint msg_count;\n\tint msg_ret;\n\tchar *msg;\n\tint ret;\n\n\tif (!tsync || !tsync->context || !tsync->msg_handle)\n\t\treturn -1;\n\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\tif (clock_context->proto_data == NULL)\n\t\treturn -1;\n\n\tptp = (struct ptp_clock_sync *)clock_context->proto_data;\n\tptp->flags = ptp_flags;\n\tmemset(&start, 0, sizeof(start));\n\tstart.series_id = htonl(ptp->series_id + 1);\n\tstart.flags = htonl(ptp->flags);\n\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME,\n\t\t\t\t\t PTP_SYNC_PKT_START, sizeof(start),\n\t\t\t\t\t (char *)&start);\n\tif (!ret)\n\t\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t\t  sync_proto, &sync_msg,\n\t\t\t\t\t\t  NULL, NULL);\n\tif (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t    sync_msg != PTP_SYNC_PKT_START)\n\t\treturn -1;\n\n\ttracefs_instance_file_write(clock_context->instance, \"trace\", \"\\0\");\n\n\tptp->series_id++;\n\tmarker.data.local_id = clock_context->local_id;\n\tmarker.data.remote_id = clock_context->remote_id;\n\tmarker.series_id = ptp->series_id;\n\tmsg = (char *)&msg_ret;\n\tsize = sizeof(msg_ret);\n\tctx.size = 2*PTP_SYNC_LOOP;\n\tctx.ptp = ptp;\n\tctx.clock = clock_context;\n\tctx.msg.count = 0;\n\tctx.msg.series_id = ptp->series_id;\n\tdo {\n\t\tmarker.data.count = count++;\n\t\tmarker.data.packet_id = 's';\n\t\tmsg_count = htonl(marker.data.count);\n\t\tptp_track_clock(&ctx, &marker);\n\t\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME,\n\t\t\t\t\t\t PTP_SYNC_PKT_PROBE,\n\t\t\t\t\t\t sizeof(msg_count),\n\t\t\t\t\t\t (char *)&msg_count);\n\t\tif (!ret)\n\t\t\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t\t\t  sync_proto, &sync_msg,\n\t\t\t\t\t\t\t  &size, &msg);\n\n\t\tmarker.data.packet_id = 'r';\n\t\tptp_track_clock(&ctx, &marker);\n\t\tif (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t\t    sync_msg != PTP_SYNC_PKT_PROBE ||\n\t\t    ntohl(msg_ret) != marker.data.count)\n\t\t\tbreak;\n\t} while (--sync_loop);\n\n\tif (sync_loop)\n\t\treturn -1;\n\n\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME,\n\t\t\t\t\t  PTP_SYNC_PKT_END, 0, NULL);\n\n\tsize = 0;\n\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t  sync_proto, &sync_msg,\n\t\t\t\t\t  &size, (char **)&results);\n\tif (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t    sync_msg != PTP_SYNC_PKT_PROBES || size == 0 || results == NULL) {\n\t\tfree(results);\n\t\treturn -1;\n\t}\n\n\tntoh_ptp_results(results);\n\tif (ptp->flags & PTP_FLAG_USE_MARKER)\n\t\ttracefs_iterate_raw_events(ptp->tep, clock_context->instance,\n\t\t\t\t\t   NULL, 0, ptp_marker_find, &ctx);\n\tif (ptp->flags & PTP_FLAG_FASTEST_RESPONSE)\n\t\tptp_calc_offset_fastest(clock_context, &ctx.msg, results, offset,\n\t\t\t\t\ttimestamp, &bad_probes);\n\telse\n\t\tptp_calc_offset_hist(clock_context, &ctx.msg, results, offset,\n\t\t\t\t     timestamp, &bad_probes);\n#ifdef TSYNC_DEBUG\n\t{\n\t\tchar buff[256];\n\t\tint res_fd;\n\n\t\tsprintf(buff, \"res-id%d.txt\", clock_context->remote_id);\n\n\t\tres_fd = open(buff, O_WRONLY|O_APPEND, 0644);\n\t\tif (res_fd > 0) {\n\t\t\tif (*offset && *timestamp) {\n\t\t\t\tsprintf(buff, \"%d %lld %lld\\n\",\n\t\t\t\t\tptp->series_id, *offset, *timestamp);\n\t\t\t\twrite(res_fd, buff, strlen(buff));\n\t\t\t}\n\t\t\tclose(res_fd);\n\t\t}\n\n\t\tprintf(\"\\n calculated offset %d: %lld, %d probes, filtered out %d, PTP flags 0x%X\\n\\r\",\n\t\t\tptp->series_id, *offset, results->count, bad_probes, ptp->flags);\n\t\tif (ptp && ptp->debug_fd > 0) {\n\t\t\tsprintf(buff, \"%lld %lld 0\\n\", *offset, *timestamp);\n\t\t\twrite(ptp->debug_fd, buff, strlen(buff));\n\t\t\tclose(ptp->debug_fd);\n\t\t\tptp->debug_fd = -1;\n\t\t}\n\n\t}\n#endif\n\n\tres_offset.offset = htonll(*offset);\n\tres_offset.ts = htonll(*timestamp);\n\tret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME,\n\t\t\t\t\t  PTP_SYNC_PKT_OFFSET,\n\t\t\t\t\t  sizeof(res_offset),\n\t\t\t\t\t  (char *)&res_offset);\n\n\tfree(results);\n\treturn 0;\n}\n\nstatic int ptp_clock_sync_calc(struct tracecmd_time_sync *tsync,\n\t\t\t       long long *offset, long long *scaling, long long *frac,\n\t\t\t       long long *timestamp, unsigned int cpu)\n{\n\tstruct clock_sync_context *clock_context;\n\tint ret;\n\n\tif (!tsync || !tsync->context)\n\t\treturn -1;\n\tclock_context = (struct clock_sync_context *)tsync->context;\n\n#ifdef TSYNC_DEBUG\n\tif (clock_context->is_server) {\n\t\tstruct ptp_clock_sync *ptp;\n\t\tchar buff[256];\n\n\t\tptp = (struct ptp_clock_sync *)clock_context->proto_data;\n\t\tif (ptp->debug_fd > 0)\n\t\t\tclose(ptp->debug_fd);\n\t\tsprintf(buff, \"s-id%d_%d.txt\",\n\t\t\t\tclock_context->remote_id, ptp->series_id+1);\n\t\tptp->debug_fd = open(buff, O_CREAT|O_WRONLY|O_TRUNC, 0644);\n\t}\n#endif\n\n\tif (scaling)\n\t\t*scaling = 1;\n\tif (frac)\n\t\t*frac = 0;\n\tif (clock_context->is_server)\n\t\tret = ptp_clock_server(tsync, offset, timestamp);\n\telse\n\t\tret = ptp_clock_client(tsync, offset, timestamp);\n\n\treturn ret;\n}\n\nint ptp_clock_sync_register(void)\n{\n\treturn tracecmd_tsync_proto_register(PTP_NAME, PTP_ACCURACY,\n\t\t\t\t\t     TRACECMD_TIME_SYNC_ROLE_GUEST |\n\t\t\t\t\t     TRACECMD_TIME_SYNC_ROLE_HOST |\n\t\t\t\t\t     TRACECMD_TIME_SYNC_ROLE_CLIENT |\n\t\t\t\t\t     TRACECMD_TIME_SYNC_ROLE_SERVER,\n\t\t\t\t\t     0, TRACECMD_TSYNC_FLAG_INTERPOLATE,\n\t\t\t\t\t     ptp_clock_sync_init,\n\t\t\t\t\t     ptp_clock_sync_free,\n\t\t\t\t\t     ptp_clock_sync_calc);\n\n}\n\nint ptp_clock_sync_unregister(void)\n{\n\treturn tracecmd_tsync_proto_unregister(PTP_NAME);\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-timesync.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n *\n */\n\n#include <fcntl.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <arpa/inet.h>\n#include <linux/limits.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <time.h>\n#include <dirent.h>\n#include <errno.h>\n#include <pthread.h>\n\n#include \"trace-cmd-private.h\"\n#include \"trace-cmd-local.h\"\n#include \"tracefs.h\"\n#include \"event-utils.h\"\n#include \"trace-tsync-local.h\"\n\nstruct tsync_proto {\n\tstruct tsync_proto *next;\n\tchar proto_name[TRACECMD_TSYNC_PNAME_LENGTH];\n\tenum tracecmd_time_sync_role roles;\n\tint accuracy;\n\tint supported_clocks;\n\tunsigned int flags;\n\n\tint (*clock_sync_init)(struct tracecmd_time_sync *clock_context);\n\tint (*clock_sync_free)(struct tracecmd_time_sync *clock_context);\n\tint (*clock_sync_calc)(struct tracecmd_time_sync *clock_context,\n\t\t\t       long long *offset, long long *scaling, long long *frac,\n\t\t\t       long long *timestamp, unsigned int cpu);\n};\n\nstruct tsync_probe_request_msg {\n\tunsigned short\tcpu;\n} __packed;\n\nstatic struct tsync_proto *tsync_proto_list;\n\nstatic struct tsync_proto *tsync_proto_find(const char *proto_name)\n{\n\tstruct tsync_proto *proto;\n\n\tif (!proto_name)\n\t\treturn NULL;\n\tfor (proto = tsync_proto_list; proto; proto = proto->next) {\n\t\tif (strlen(proto->proto_name) == strlen(proto_name) &&\n\t\t     !strncmp(proto->proto_name, proto_name, TRACECMD_TSYNC_PNAME_LENGTH))\n\t\t\treturn proto;\n\t}\n\treturn NULL;\n}\n\n/**\n * tracecmd_tsync_init - Initialize the global, per task, time sync data.\n */\nvoid tracecmd_tsync_init(void)\n{\n\tptp_clock_sync_register();\n\tkvm_clock_sync_register();\n}\n\nint tracecmd_tsync_proto_register(const char *proto_name, int accuracy, int roles,\n\t\t\t\t  int supported_clocks, unsigned int flags,\n\t\t\t\t  int (*init)(struct tracecmd_time_sync *),\n\t\t\t\t  int (*free)(struct tracecmd_time_sync *),\n\t\t\t\t  int (*calc)(struct tracecmd_time_sync *,\n\t\t\t\t\t      long long *, long long *, long long *,\n\t\t\t\t\t      long long *, unsigned int))\n{\n\tstruct tsync_proto *proto = NULL;\n\n\tif (tsync_proto_find(proto_name))\n\t\treturn -1;\n\tproto = calloc(1, sizeof(struct tsync_proto));\n\tif (!proto)\n\t\treturn -1;\n\tstrncpy(proto->proto_name, proto_name, TRACECMD_TSYNC_PNAME_LENGTH);\n\tproto->accuracy = accuracy;\n\tproto->roles = roles;\n\tproto->flags = flags;\n\tproto->supported_clocks = supported_clocks;\n\tproto->clock_sync_init = init;\n\tproto->clock_sync_free = free;\n\tproto->clock_sync_calc = calc;\n\n\tproto->next = tsync_proto_list;\n\ttsync_proto_list = proto;\n\treturn 0;\n}\n\nint tracecmd_tsync_proto_unregister(char *proto_name)\n{\n\tstruct tsync_proto **last = &tsync_proto_list;\n\n\tif (!proto_name)\n\t\treturn -1;\n\n\tfor (; *last; last = &(*last)->next) {\n\t\tif (strlen((*last)->proto_name) == strlen(proto_name) &&\n\t\t    !strncmp((*last)->proto_name, proto_name, TRACECMD_TSYNC_PNAME_LENGTH)) {\n\t\t\tstruct tsync_proto *proto = *last;\n\n\t\t\t*last = proto->next;\n\t\t\tfree(proto);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nbool __hidden tcmd_tsync_proto_is_supported(const char *proto_name)\n{\n\tif (tsync_proto_find(proto_name))\n\t\treturn true;\n\treturn false;\n}\n\n/**\n * tracecmd_tsync_get_offsets - Return the calculated time offsets\n *\n * @tsync: Pointer to time sync context\n * @cpu: CPU for which to get the calculated offsets\n * @count: Returns the number of calculated time offsets\n * @ts: Array of size @count containing timestamps of callculated offsets\n * @offsets: array of size @count, containing offsets for each timestamp\n * @scalings: array of size @count, containing scaling ratios for each timestamp\n * @frac: array of size @count, containing fraction bits for each timestamp\n *\n * Retuns -1 in case of an error, or 0 otherwise\n */\nint tracecmd_tsync_get_offsets(struct tracecmd_time_sync *tsync, int cpu,\n\t\t\t       int *count, long long **ts,\n\t\t\t       long long **offsets, long long **scalings, long long **frac)\n{\n\tstruct clock_sync_context *tsync_context;\n\n\tif (!tsync || !tsync->context)\n\t\treturn -1;\n\ttsync_context = (struct clock_sync_context *)tsync->context;\n\tif (cpu >= tsync_context->cpu_count || !tsync_context->offsets)\n\t\treturn -1;\n\tif (count)\n\t\t*count = tsync_context->offsets[cpu].sync_count;\n\tif (ts)\n\t\t*ts = tsync_context->offsets[cpu].sync_ts;\n\tif (offsets)\n\t\t*offsets = tsync_context->offsets[cpu].sync_offsets;\n\tif (scalings)\n\t\t*scalings = tsync_context->offsets[cpu].sync_scalings;\n\tif (frac)\n\t\t*frac = tsync_context->offsets[cpu].sync_frac;\n\n\treturn 0;\n}\n\n/**\n * tsync_get_proto_flags - Get protocol flags\n *\n * @tsync: Pointer to time sync context\n * @flags: Returns the protocol flags, a combination of TRACECMD_TSYNC_FLAG_...\n *\n * Retuns -1 in case of an error, or 0 otherwise\n */\nstatic int tsync_get_proto_flags(struct tracecmd_time_sync *tsync,\n\t\t\t\t unsigned int *flags)\n{\n\tstruct tsync_proto *protocol;\n\n\tif (!tsync)\n\t\treturn -1;\n\tprotocol = tsync_proto_find(tsync->proto_name);\n\tif (!protocol)\n\t\treturn -1;\n\n\tif (flags)\n\t\t*flags = protocol->flags;\n\n\treturn 0;\n}\n\n\n#define PROTO_MASK_SIZE (sizeof(char))\n#define PROTO_MASK_BITS (PROTO_MASK_SIZE * 8)\n/**\n * tsync_proto_select - Select time sync protocol, to be used for\n *\t\ttimestamp synchronization with a peer\n *\n * @protos: list of tsync protocol names\n * @clock : trace clock\n * @role : local time sync role\n *\n * Retuns pointer to a protocol name, that can be used with the peer, or NULL\n *\t  in case there is no match with supported protocols.\n *\t  The returned string MUST NOT be freed by the caller\n */\nstatic const char *\ntsync_proto_select(const struct tracecmd_tsync_protos *protos,\n\t\t   const char *clock, enum tracecmd_time_sync_role role)\n{\n\tstruct tsync_proto *selected = NULL;\n\tstruct tsync_proto *proto;\n\tchar **pname;\n\tint clock_id = 0;\n\n\tif (!protos)\n\t\treturn NULL;\n\n\tclock_id = tracecmd_clock_str2id(clock);\n\tpname = protos->names;\n\twhile (*pname) {\n\t\tfor (proto = tsync_proto_list; proto; proto = proto->next) {\n\t\t\tif (!(proto->roles & role))\n\t\t\t\tcontinue;\n\t\t\tif (proto->supported_clocks && clock_id &&\n\t\t\t    !(proto->supported_clocks & clock_id))\n\t\t\t\tcontinue;\n\t\t\tif (strncmp(proto->proto_name, *pname, TRACECMD_TSYNC_PNAME_LENGTH))\n\t\t\t\tcontinue;\n\t\t\tif (selected) {\n\t\t\t\tif (selected->accuracy > proto->accuracy)\n\t\t\t\t\tselected = proto;\n\t\t\t} else\n\t\t\t\tselected = proto;\n\t\t}\n\t\tpname++;\n\t}\n\n\tif (selected)\n\t\treturn selected->proto_name;\n\n\treturn NULL;\n}\n\n/**\n * tracecmd_tsync_get_proto - return the appropriate synchronization protocol\n * @protos: The list of synchronization protocols to choose from\n * @clock: The clock that is being used (or NULL for unknown).\n *\n * Retuns pointer to a protocol name, that can be used with the peer, or NULL\n *\t  in case there is no match with supported protocols.\n *\t  The returned string MUST NOT be freed by the caller\n */\n__hidden const char *\ntracecmd_tsync_get_proto(const struct tracecmd_tsync_protos *protos,\n\t\t\t const char *clock, enum tracecmd_time_sync_role role)\n{\n\treturn tsync_proto_select(protos, clock, role);\n}\n\n/**\n * tracecmd_tsync_proto_getall - Returns list of all supported\n *\t\t\t\t time sync protocols\n * @protos: return, allocated list of time sync protocol names,\n *\t       supported by the peer. Must be freed by free()\n * @clock: selected trace clock\n * @role: supported protocol role\n *\n * If completed successfully 0 is returned and allocated list of strings in @protos.\n * The last list entry is NULL. In case of an error, -1 is returned.\n * @protos must be freed with free()\n */\nint tracecmd_tsync_proto_getall(struct tracecmd_tsync_protos **protos, const char *clock, int role)\n{\n\tstruct tracecmd_tsync_protos *plist = NULL;\n\tstruct tsync_proto *proto;\n\tint clock_id = 0;\n\tint count = 1;\n\tint i;\n\n\tif (clock)\n\t\tclock_id =  tracecmd_clock_str2id(clock);\n\tfor (proto = tsync_proto_list; proto; proto = proto->next) {\n\t\tif (!(proto->roles & role))\n\t\t\tcontinue;\n\t\tif (proto->supported_clocks && clock_id &&\n\t\t    !(proto->supported_clocks & clock_id))\n\t\t\tcontinue;\n\t\tcount++;\n\t}\n\tplist = calloc(1, sizeof(struct tracecmd_tsync_protos));\n\tif (!plist)\n\t\tgoto error;\n\tplist->names = calloc(count, sizeof(char *));\n\tif (!plist->names)\n\t\tgoto error;\n\n\tfor (i = 0, proto = tsync_proto_list; proto && i < (count - 1); proto = proto->next) {\n\t\tif (!(proto->roles & role))\n\t\t\tcontinue;\n\t\tif (proto->supported_clocks && clock_id &&\n\t\t    !(proto->supported_clocks & clock_id))\n\t\t\tcontinue;\n\t\tplist->names[i++] = proto->proto_name;\n\t}\n\n\t*protos = plist;\n\treturn 0;\n\nerror:\n\tif (plist) {\n\t\tfree(plist->names);\n\t\tfree(plist);\n\t}\n\treturn -1;\n}\n\nstatic int get_first_cpu(cpu_set_t **pin_mask, size_t *m_size)\n{\n\tint cpus = tracecmd_count_cpus();\n\tcpu_set_t *cpu_mask;\n\tint mask_size;\n\tint i;\n\n\tcpu_mask = CPU_ALLOC(cpus);\n\t*pin_mask = CPU_ALLOC(cpus);\n\tif (!cpu_mask || !*pin_mask || 1)\n\t\tgoto error;\n\n\tmask_size = CPU_ALLOC_SIZE(cpus);\n\tCPU_ZERO_S(mask_size, cpu_mask);\n\tCPU_ZERO_S(mask_size, *pin_mask);\n\n\tif (sched_getaffinity(0, mask_size, cpu_mask) == -1)\n\t\tgoto error;\n\n\tfor (i = 0; i < cpus; i++) {\n\t\tif (CPU_ISSET_S(i, mask_size, cpu_mask)) {\n\t\t\tCPU_SET_S(i, mask_size, *pin_mask);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (CPU_COUNT_S(mask_size, *pin_mask) < 1)\n\t\tgoto error;\n\n\tCPU_FREE(cpu_mask);\n\t*m_size = mask_size;\n\treturn 0;\n\nerror:\n\tif (cpu_mask)\n\t\tCPU_FREE(cpu_mask);\n\tif (*pin_mask)\n\t\tCPU_FREE(*pin_mask);\n\t*pin_mask = NULL;\n\t*m_size = 0;\n\treturn -1;\n}\n\nstatic struct tracefs_instance *\nclock_synch_create_instance(const char *clock, unsigned int cid)\n{\n\tstruct tracefs_instance *instance;\n\tchar inst_name[256];\n\n\tsnprintf(inst_name, 256, \"clock_synch-%d\", cid);\n\n\tinstance = tracefs_instance_create(inst_name);\n\tif (!instance)\n\t\treturn NULL;\n\n\ttracefs_instance_file_write(instance, \"trace\", \"\\0\");\n\tif (clock)\n\t\ttracefs_instance_file_write(instance, \"trace_clock\", clock);\n\treturn instance;\n}\n\nstatic void\nclock_synch_delete_instance(struct tracefs_instance *inst)\n{\n\tif (!inst)\n\t\treturn;\n\ttracefs_instance_destroy(inst);\n\ttracefs_instance_free(inst);\n}\n\nstatic int clock_context_init(struct tracecmd_time_sync *tsync, bool guest)\n{\n\tstruct clock_sync_context *clock = NULL;\n\tstruct tsync_proto *protocol;\n\n\tif (tsync->context)\n\t\treturn 0;\n\n\tprotocol = tsync_proto_find(tsync->proto_name);\n\tif (!protocol || !protocol->clock_sync_calc)\n\t\treturn -1;\n\n\tclock = calloc(1, sizeof(struct clock_sync_context));\n\tif (!clock)\n\t\treturn -1;\n\tclock->is_guest = guest;\n\tclock->is_server = clock->is_guest;\n\n\tclock->instance = clock_synch_create_instance(tsync->clock_str,\n\t\t\t\t\t\t      tsync->remote_id);\n\tif (!clock->instance)\n\t\tgoto error;\n\n\tclock->cpu_count = tsync->vcpu_count;\n\tif (clock->cpu_count) {\n\t\tclock->offsets = calloc(clock->cpu_count, sizeof(struct clock_sync_offsets));\n\t\tif (!clock->offsets)\n\t\t\tgoto error;\n\t}\n\n\ttsync->context = clock;\n\tif (protocol->clock_sync_init && protocol->clock_sync_init(tsync) < 0)\n\t\tgoto error;\n\n\ttsync->proto = protocol;\n\n\treturn 0;\nerror:\n\ttsync->context = NULL;\n\tif (clock->instance)\n\t\tclock_synch_delete_instance(clock->instance);\n\tfree(clock->offsets);\n\tfree(clock);\n\treturn -1;\n}\n\n/**\n * tracecmd_tsync_free - Free time sync context, allocated by\n *\t\ttracecmd_tsync_with_host() or tracecmd_tsync_with_guest() APIs\n *\n * @tsync: Pointer to time sync context\n *\n */\nvoid tracecmd_tsync_free(struct tracecmd_time_sync *tsync)\n{\n\tstruct clock_sync_context *tsync_context;\n\tstruct tsync_proto *proto;\n\tint i;\n\n\tif (!tsync)\n\t\treturn;\n\n\ttsync_context = (struct clock_sync_context *)tsync->context;\n\n\tproto = tsync_proto_find(tsync->proto_name);\n\tif (proto && proto->clock_sync_free)\n\t\tproto->clock_sync_free(tsync);\n\n\n\tif (tsync_context) {\n\t\tclock_synch_delete_instance(tsync_context->instance);\n\t\ttsync_context->instance = NULL;\n\n\t\tif (tsync_context->cpu_count && tsync_context->offsets) {\n\t\t\tfor (i = 0; i < tsync_context->cpu_count; i++) {\n\t\t\t\tfree(tsync_context->offsets[i].sync_ts);\n\t\t\t\tfree(tsync_context->offsets[i].sync_offsets);\n\t\t\t\tfree(tsync_context->offsets[i].sync_scalings);\n\t\t\t\tfree(tsync_context->offsets[i].sync_frac);\n\t\t\t\ttsync_context->offsets[i].sync_ts = NULL;\n\t\t\t\ttsync_context->offsets[i].sync_offsets = NULL;\n\t\t\t\ttsync_context->offsets[i].sync_scalings = NULL;\n\t\t\t\ttsync_context->offsets[i].sync_frac = NULL;\n\t\t\t\ttsync_context->offsets[i].sync_count = 0;\n\t\t\t\ttsync_context->offsets[i].sync_size = 0;\n\t\t\t}\n\t\t\tfree(tsync_context->offsets);\n\t\t\ttsync_context->offsets = NULL;\n\t\t}\n\t}\n\n\tif (tsync->msg_handle)\n\t\ttracecmd_msg_handle_close(tsync->msg_handle);\n\n\t/* These are only created from the host */\n\tif (tsync->guest_pid) {\n\t\tpthread_mutex_destroy(&tsync->lock);\n\t\tpthread_cond_destroy(&tsync->cond);\n\t\tpthread_barrier_destroy(&tsync->first_sync);\n\t}\n\n\tfree(tsync->clock_str);\n\tfree(tsync->proto_name);\n\tfree(tsync);\n}\n\nstatic cpu_set_t *pin_to_cpu(int cpu)\n{\n\tstatic size_t size;\n\tstatic int cpus;\n\tcpu_set_t *mask = NULL;\n\tcpu_set_t *old = NULL;\n\n\tif (!cpus) {\n\t\tcpus = tracecmd_count_cpus();\n\t\tsize = CPU_ALLOC_SIZE(cpus);\n\t}\n\tif (cpu >= cpus)\n\t\tgoto error;\n\n\tmask = CPU_ALLOC(cpus);\n\tif (!mask)\n\t\tgoto error;\n\told = CPU_ALLOC(cpus);\n\tif (!old)\n\t\tgoto error;\n\n\tCPU_ZERO_S(size, mask);\n\tCPU_SET_S(cpu, size, mask);\n\tif (pthread_getaffinity_np(pthread_self(), size, old))\n\t\tgoto error;\n\tif (pthread_setaffinity_np(pthread_self(), size, mask))\n\t\tgoto error;\n\n\tCPU_FREE(mask);\n\treturn old;\n\nerror:\n\tif (mask)\n\t\tCPU_FREE(mask);\n\tif (old)\n\t\tCPU_FREE(old);\n\treturn NULL;\n}\n\nstatic void restore_pin_to_cpu(cpu_set_t *mask)\n{\n\tstatic size_t size;\n\n\tif (!size)\n\t\tsize = CPU_ALLOC_SIZE(tracecmd_count_cpus());\n\n\tpthread_setaffinity_np(pthread_self(), size, mask);\n\tCPU_FREE(mask);\n}\n\nstatic int tsync_send(struct tracecmd_time_sync *tsync, unsigned int cpu)\n{\n\tstruct tsync_proto *proto = tsync->proto;\n\tcpu_set_t *old_set = NULL;\n\tlong long timestamp = 0;\n\tlong long scaling = 0;\n\tlong long offset = 0;\n\tlong long frac = 0;\n\tint ret;\n\n\told_set = pin_to_cpu(cpu);\n\tret = proto->clock_sync_calc(tsync, &offset, &scaling, &frac, &timestamp, cpu);\n\tif (old_set)\n\t\trestore_pin_to_cpu(old_set);\n\n\treturn ret;\n}\n\nstatic void tsync_with_host(struct tracecmd_time_sync *tsync)\n{\n\tchar protocol[TRACECMD_TSYNC_PNAME_LENGTH];\n\tstruct tsync_probe_request_msg probe;\n\tunsigned int command;\n\tunsigned int size;\n\tchar *msg;\n\tint ret;\n\n\tmsg = (char *)&probe;\n\tsize = sizeof(probe);\n\twhile (true) {\n\t\tmemset(&probe, 0, size);\n\t\tret = tracecmd_msg_recv_time_sync(tsync->msg_handle,\n\t\t\t\t\t\t  protocol, &command,\n\t\t\t\t\t\t  &size, &msg);\n\n\t\tif (ret || strncmp(protocol, TRACECMD_TSYNC_PROTO_NONE, TRACECMD_TSYNC_PNAME_LENGTH) ||\n\t\t    command != TRACECMD_TIME_SYNC_CMD_PROBE)\n\t\t\tbreak;\n\t\tret = tsync_send(tsync, probe.cpu);\n\t\tif (ret)\n\t\t\tbreak;\n\t}\n}\n\nstatic int record_sync_sample(struct clock_sync_offsets *offsets, int array_step,\n\t\t\t      long long offset, long long scaling, long long frac, long long ts)\n{\n\tlong long *sync_scalings = NULL;\n\tlong long *sync_offsets = NULL;\n\tlong long *sync_frac = NULL;\n\tlong long *sync_ts = NULL;\n\n\tif (offsets->sync_count >= offsets->sync_size) {\n\t\tsync_ts = realloc(offsets->sync_ts,\n\t\t\t\t  (offsets->sync_size + array_step) * sizeof(long long));\n\t\tsync_offsets = realloc(offsets->sync_offsets,\n\t\t\t\t       (offsets->sync_size + array_step) * sizeof(long long));\n\t\tsync_scalings = realloc(offsets->sync_scalings,\n\t\t\t\t       (offsets->sync_size + array_step) * sizeof(long long));\n\t\tsync_frac = realloc(offsets->sync_frac,\n\t\t\t\t    (offsets->sync_size + array_step) * sizeof(long long));\n\n\t\tif (!sync_ts || !sync_offsets || !sync_scalings || !sync_frac) {\n\t\t\tfree(sync_ts);\n\t\t\tfree(sync_offsets);\n\t\t\tfree(sync_scalings);\n\t\t\tfree(sync_frac);\n\t\t\treturn -1;\n\t\t}\n\t\toffsets->sync_size += array_step;\n\t\toffsets->sync_ts = sync_ts;\n\t\toffsets->sync_offsets = sync_offsets;\n\t\toffsets->sync_scalings = sync_scalings;\n\t\toffsets->sync_frac = sync_frac;\n\t}\n\n\toffsets->sync_ts[offsets->sync_count] = ts;\n\toffsets->sync_offsets[offsets->sync_count] = offset;\n\toffsets->sync_scalings[offsets->sync_count] = scaling;\n\toffsets->sync_frac[offsets->sync_count] = frac;\n\toffsets->sync_count++;\n\n\treturn 0;\n}\n\nstatic int tsync_get_sample(struct tracecmd_time_sync *tsync, unsigned int cpu,\n\t\t\t    int array_step)\n{\n\tstruct tsync_proto *proto = tsync->proto;\n\tstruct clock_sync_context *clock;\n\tlong long timestamp = 0;\n\tlong long scaling = 0;\n\tlong long offset = 0;\n\tlong long frac = 0;\n\tint ret;\n\n\tret = proto->clock_sync_calc(tsync, &offset, &scaling, &frac, &timestamp, cpu);\n\tif (ret) {\n\t\ttracecmd_warning(\"Failed to synchronize timestamps with guest\");\n\t\treturn -1;\n\t}\n\tif (!offset || !timestamp || !scaling)\n\t\treturn 0;\n\tclock = tsync->context;\n\tif (!clock || cpu >= clock->cpu_count || !clock->offsets)\n\t\treturn -1;\n\treturn record_sync_sample(&clock->offsets[cpu], array_step,\n\t\t\t\t  offset, scaling, frac, timestamp);\n}\n\n#define TIMER_SEC_NANO 1000000000LL\nstatic inline void get_ts_loop_delay(struct timespec *timeout, int delay_ms)\n{\n\tmemset(timeout, 0, sizeof(struct timespec));\n\tclock_gettime(CLOCK_REALTIME, timeout);\n\n\ttimeout->tv_nsec += ((unsigned long long)delay_ms * 1000000LL);\n\n\tif (timeout->tv_nsec >= TIMER_SEC_NANO) {\n\t\ttimeout->tv_sec += timeout->tv_nsec / TIMER_SEC_NANO;\n\t\ttimeout->tv_nsec %= TIMER_SEC_NANO;\n\t}\n}\n\n#define CLOCK_TS_ARRAY 5\nstatic int tsync_with_guest(struct tracecmd_time_sync *tsync)\n{\n\tstruct tsync_probe_request_msg probe;\n\tint ts_array_size = CLOCK_TS_ARRAY;\n\tstruct timespec timeout;\n\tbool first = true;\n\tbool end = false;\n\tint ret;\n\tint i;\n\n\tif (tsync->loop_interval > 0 &&\n\t    tsync->loop_interval < (CLOCK_TS_ARRAY * 1000))\n\t\tts_array_size = (CLOCK_TS_ARRAY * 1000) / tsync->loop_interval;\n\n\twhile (true) {\n\t\tpthread_mutex_lock(&tsync->lock);\n\t\tfor (i = 0; i < tsync->vcpu_count; i++) {\n\t\t\tprobe.cpu = i;\n\t\t\tret = tracecmd_msg_send_time_sync(tsync->msg_handle,\n\t\t\t\t\t\t\t  TRACECMD_TSYNC_PROTO_NONE,\n\t\t\t\t\t\t\t  TRACECMD_TIME_SYNC_CMD_PROBE,\n\t\t\t\t\t\t\t  sizeof(probe), (char *)&probe);\n\t\t\tret = tsync_get_sample(tsync, i, ts_array_size);\n\t\t\tif (ret)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (first) {\n\t\t\tfirst = false;\n\t\t\tpthread_barrier_wait(&tsync->first_sync);\n\t\t}\n\t\tif (end || i < tsync->vcpu_count) {\n\t\t\tpthread_mutex_unlock(&tsync->lock);\n\t\t\tbreak;\n\t\t}\n\t\tif (tsync->loop_interval > 0) {\n\t\t\tget_ts_loop_delay(&timeout, tsync->loop_interval);\n\t\t\tret = pthread_cond_timedwait(&tsync->cond, &tsync->lock, &timeout);\n\t\t\tpthread_mutex_unlock(&tsync->lock);\n\t\t\tif (ret && ret != ETIMEDOUT)\n\t\t\t\tbreak;\n\t\t\telse if (!ret)\n\t\t\t\tend = true;\n\t\t} else {\n\t\t\tpthread_cond_wait(&tsync->cond, &tsync->lock);\n\t\t\tend = true;\n\t\t\tpthread_mutex_unlock(&tsync->lock);\n\t\t}\n\t};\n\n\ttracecmd_msg_send_time_sync(tsync->msg_handle,\n\t\t\t\t    TRACECMD_TSYNC_PROTO_NONE,\n\t\t\t\t    TRACECMD_TIME_SYNC_CMD_STOP,\n\t\t\t\t    0, NULL);\n\treturn 0;\n}\n\nstatic void *tsync_host_thread(void *data)\n{\n\tstruct tracecmd_time_sync *tsync = data;\n\n\ttsync_with_guest(tsync);\n\tpthread_exit(0);\n}\n\n/**\n * tracecmd_tsync_with_guest - Synchronize timestamps with guest\n *\n * @trace_id: Local ID for the current trace session\n * @fd: file descriptor of guest\n * @guest_pid: PID of the host OS process, running the guest\n * @guest_cpus: Number of the guest VCPUs\n * @proto_name: Name of the negotiated time synchronization protocol\n * @clock: Trace clock, used for that session\n *\n * On success, a pointer to time sync context is returned, or NULL in\n * case of an error. The context must be freed with tracecmd_tsync_free()\n *\n * This API spawns a pthread, which performs time stamps synchronization\n * until tracecmd_tsync_with_guest_stop() is called.\n */\nstruct tracecmd_time_sync *\ntracecmd_tsync_with_guest(unsigned long long trace_id, int loop_interval,\n\t\t\t  unsigned int fd, int guest_pid,\n\t\t\t  int guest_cpus, const char *proto_name, const char *clock)\n{\n\tstruct tracecmd_time_sync *tsync;\n\tcpu_set_t *pin_mask = NULL;\n\tpthread_attr_t attrib;\n\tsize_t mask_size = 0;\n\tint ret;\n\n\tif (!proto_name)\n\t\treturn NULL;\n\n\ttsync = calloc(1, sizeof(*tsync));\n\tif (!tsync)\n\t\treturn NULL;\n\n\ttsync->trace_id = trace_id;\n\ttsync->loop_interval = loop_interval;\n\ttsync->proto_name = strdup(proto_name);\n\n\ttsync->msg_handle = tracecmd_msg_handle_alloc(fd, 0);\n\tif (!tsync->msg_handle) {\n\t\tret = -1;\n\t\tgoto error;\n\t}\n\ttsync->guest_pid = guest_pid;\n\ttsync->vcpu_count = guest_cpus;\n\n\tif (clock)\n\t\ttsync->clock_str = strdup(clock);\n\tpthread_mutex_init(&tsync->lock, NULL);\n\tpthread_cond_init(&tsync->cond, NULL);\n\tpthread_barrier_init(&tsync->first_sync, NULL, 2);\n\tpthread_attr_init(&attrib);\n\tpthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);\n\n\tclock_context_init(tsync, false);\n\tif (!tsync->context)\n\t\tgoto error;\n\n\tret = pthread_create(&tsync->thread, &attrib, tsync_host_thread, tsync);\n\tif (ret)\n\t\tgoto error;\n\ttsync->thread_running = true;\n\n\tif (!get_first_cpu(&pin_mask, &mask_size))\n\t\tpthread_setaffinity_np(tsync->thread, mask_size, pin_mask);\n\tpthread_barrier_wait(&tsync->first_sync);\n\n\tif (pin_mask)\n\t\tCPU_FREE(pin_mask);\n\tpthread_attr_destroy(&attrib);\n\n\treturn tsync;\n\nerror:\n\tif (tsync->msg_handle)\n\t\ttracecmd_msg_handle_close(tsync->msg_handle);\n\telse if (fd >= 0)\n\t\tclose(fd);\n\tfree(tsync->proto_name);\n\tfree(tsync);\n\n\treturn NULL;\n}\n\n/**\n * tracecmd_write_guest_time_shift - Write collected timestamp corrections in a file\n *\n * @handle: Handle to a trace file, where timestamp corrections will be saved\n * @tsync: Time sync context with collected timestamp corrections\n *\n * Returns 0 on success, or -1 in case of an error.\n *\n * This API writes collected timestamp corrections in the metadata of the\n * trace file, as TRACECMD_OPTION_TIME_SHIFT option.\n */\nint tracecmd_write_guest_time_shift(struct tracecmd_output *handle,\n\t\t\t\t    struct tracecmd_time_sync *tsync)\n{\n\tstruct iovec *vector = NULL;\n\tunsigned int flags;\n\tlong long *scalings = NULL;\n\tlong long *offsets = NULL;\n\tlong long *frac = NULL;\n\tlong long *ts = NULL;\n\tint vcount;\n\tint count;\n\tint i, j;\n\tint ret = -1;\n\n\tif (!tsync || !tsync->vcpu_count)\n\t\treturn -1;\n\tvcount = 3 + (5 * tsync->vcpu_count);\n\tvector = calloc(vcount, sizeof(struct iovec));\n\tif (!vector)\n\t\treturn -1;\n\tret = tsync_get_proto_flags(tsync, &flags);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tj = 0;\n\tvector[j].iov_len = 8;\n\tvector[j++].iov_base = &tsync->trace_id;\n\tvector[j].iov_len = 4;\n\tvector[j++].iov_base = &flags;\n\tvector[j].iov_len = 4;\n\tvector[j++].iov_base = &tsync->vcpu_count;\n\tfor (i = 0; i < tsync->vcpu_count; i++) {\n\t\tif (j >= vcount)\n\t\t\tbreak;\n\t\tret = tracecmd_tsync_get_offsets(tsync, i, &count,\n\t\t\t\t\t\t &ts, &offsets, &scalings, NULL);\n\t\tif (ret < 0 || !count || !ts || !offsets || !scalings)\n\t\t\tbreak;\n\t\tvector[j].iov_len = 4;\n\t\tvector[j++].iov_base = &count;\n\t\tvector[j].iov_len = 8 * count;\n\t\tvector[j++].iov_base = ts;\n\t\tvector[j].iov_len = 8 * count;\n\t\tvector[j++].iov_base = offsets;\n\t\tvector[j].iov_len = 8 * count;\n\t\tvector[j++].iov_base = scalings;\n\t}\n\tif (i < tsync->vcpu_count) {\n\t\tret = -1;\n\t\tgoto out;\n\t}\n\t/*\n\t * Writing fraction bits into the option is implemented in a separate loop for\n\t * backward compatibility. In the trace-cmd 2.9 release, this option has only offset\n\t * and scaling. That legacy code must work with the new extended option.\n\t *\n\t */\n\tfor (i = 0; i < tsync->vcpu_count; i++) {\n\t\tif (j >= vcount)\n\t\t\tbreak;\n\t\tret = tracecmd_tsync_get_offsets(tsync, i, NULL,\n\t\t\t\t\t\t NULL, NULL, NULL, &frac);\n\t\tif (ret < 0)\n\t\t\tbreak;\n\t\tvector[j].iov_len = 8 * count;\n\t\tvector[j++].iov_base = frac;\n\t}\n\tif (i < tsync->vcpu_count) {\n\t\tret = -1;\n\t\tgoto out;\n\t}\n\n\ttracecmd_add_option_v(handle, TRACECMD_OPTION_TIME_SHIFT, vector, vcount);\n#ifdef TSYNC_DEBUG\n\tif (count > 1)\n\t\tprintf(\"Got %d timestamp synch samples in %lld ns trace\\n\\r\",\n\t\t\tcount, ts[count - 1] - ts[0]);\n#endif\n\tret = 0;\nout:\n\tfree(vector);\n\treturn ret;\n}\n\n/**\n * tracecmd_tsync_with_guest_stop - Stop the time sync session with a guest\n *\n * @tsync: Time sync context, representing a running time sync session\n *\n * Returns 0 on success, or -1 in case of an error.\n *\n */\nint tracecmd_tsync_with_guest_stop(struct tracecmd_time_sync *tsync)\n{\n\tif (!tsync || !tsync->thread_running)\n\t\treturn -1;\n\n\t/* Signal the time synchronization thread to complete and wait for it */\n\tpthread_mutex_lock(&tsync->lock);\n\tpthread_cond_signal(&tsync->cond);\n\tpthread_mutex_unlock(&tsync->lock);\n\tpthread_join(tsync->thread, NULL);\n\treturn 0;\n}\n\nstatic void *tsync_agent_thread(void *data)\n{\n\tstruct tracecmd_time_sync *tsync = data;\n\n\ttsync_with_host(tsync);\n\tpthread_exit(NULL);\n}\n\n/**\n * tracecmd_tsync_with_host - Synchronize timestamps with host\n * @fd: File descriptor connecting with the host\n * @proto: The selected protocol\n * @clock: Trace clock, used for that session\n * @port: returned, VSOCKET port, on which the guest listens for tsync requests\n * @remote_id: Identifier to uniquely identify the remote host\n * @local_id: Identifier to uniquely identify the local machine\n *\n * On success, a pointer to time sync context is returned, or NULL in\n * case of an error. The context must be freed with tracecmd_tsync_free()\n *\n * This API spawns a pthread, which performs time stamps synchronization\n * until tracecmd_tsync_with_host_stop() is called.\n */\nstruct tracecmd_time_sync *\ntracecmd_tsync_with_host(int fd, const char *proto, const char *clock,\n\t\t\t int remote_id, int local_id)\n{\n\tstruct tracecmd_time_sync *tsync;\n\tcpu_set_t *pin_mask = NULL;\n\tpthread_attr_t attrib;\n\tsize_t mask_size = 0;\n\tint ret;\n\n\ttsync = calloc(1, sizeof(struct tracecmd_time_sync));\n\tif (!tsync)\n\t\treturn NULL;\n\n\ttsync->proto_name = strdup(proto);\n\ttsync->msg_handle = tracecmd_msg_handle_alloc(fd, 0);\n\tif (clock)\n\t\ttsync->clock_str = strdup(clock);\n\n\ttsync->remote_id = remote_id;\n\ttsync->local_id = local_id;\n\n\tpthread_attr_init(&attrib);\n\ttsync->vcpu_count = tracecmd_count_cpus();\n\tpthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);\n\n\tclock_context_init(tsync, true);\n\tif (!tsync->context)\n\t\tgoto error;\n\n\tret = pthread_create(&tsync->thread, &attrib, tsync_agent_thread, tsync);\n\tif (ret) {\n\t\tpthread_attr_destroy(&attrib);\n\t\tgoto error;\n\t}\n\ttsync->thread_running = true;\n\tif (!get_first_cpu(&pin_mask, &mask_size))\n\t\tpthread_setaffinity_np(tsync->thread, mask_size, pin_mask);\n\n\tif (pin_mask)\n\t\tCPU_FREE(pin_mask);\n\tpthread_attr_destroy(&attrib);\n\treturn tsync;\n\nerror:\n\tif (tsync) {\n\t\tif (tsync->msg_handle) {\n\t\t\t/* Do not close the fd that was passed it */\n\t\t\ttsync->msg_handle->fd = -1;\n\t\t\ttracecmd_msg_handle_close(tsync->msg_handle);\n\t\t}\n\t\tfree(tsync->clock_str);\n\t\tfree(tsync);\n\t}\n\n\treturn NULL;\n\n}\n\n/**\n * tracecmd_tsync_with_host_stop - Stop the time sync session with a host\n *\n * @tsync: Time sync context, representing a running time sync session\n *\n * Returns 0 on success, or error number in case of an error.\n *\n */\nint tracecmd_tsync_with_host_stop(struct tracecmd_time_sync *tsync)\n{\n\treturn pthread_join(tsync->thread, NULL);\n}\n"
  },
  {
    "path": "lib/trace-cmd/trace-util.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <dirent.h>\n#include <ctype.h>\n#include <errno.h>\n#include <dlfcn.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <limits.h>\n#include <libgen.h>\n#include <sys/mount.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/sysinfo.h>\n#include <time.h>\n#include <event-parse.h>\n#include <event-utils.h>\n\n#include \"trace-cmd-private.h\"\n#include \"trace-cmd-local.h\"\n\n#define LOCAL_PLUGIN_DIR \".trace-cmd/plugins\"\n#define PROC_STACK_FILE \"/proc/sys/kernel/stack_tracer_enabled\"\n\nstatic bool debug;\nstatic bool notimeout;\nstatic int log_level = TEP_LOG_WARNING;\nstatic FILE *logfp;\n\nconst static struct {\n\tconst char *clock_str;\n\tenum tracecmd_clocks clock_id;\n} trace_clocks[] = {\n\t{\"local\", TRACECMD_CLOCK_LOCAL},\n\t{\"global\", TRACECMD_CLOCK_GLOBAL},\n\t{\"counter\", TRACECMD_CLOCK_COUNTER},\n\t{\"uptime\", TRACECMD_CLOCK_UPTIME},\n\t{\"perf\", TRACECMD_CLOCK_PERF},\n\t{\"mono\", TRACECMD_CLOCK_MONO},\n\t{\"mono_raw\", TRACECMD_CLOCK_MONO_RAW},\n\t{\"boot\", TRACECMD_CLOCK_BOOT},\n\t{\"x86-tsc\", TRACECMD_CLOCK_X86_TSC},\n\t{NULL, -1}\n};\n\n/**\n * tracecmd_clock_str2id - Convert ftrace clock name to clock ID\n * @clock: Ftrace clock name\n * Returns ID of the ftrace clock\n */\nenum tracecmd_clocks tracecmd_clock_str2id(const char *clock)\n{\n\tint i;\n\n\tif (!clock)\n\t\treturn TRACECMD_CLOCK_UNKNOWN;\n\n\tfor (i = 0; trace_clocks[i].clock_str; i++) {\n\t\tif (!strncmp(clock, trace_clocks[i].clock_str,\n\t\t    strlen(trace_clocks[i].clock_str)))\n\t\t\treturn trace_clocks[i].clock_id;\n\t}\n\treturn TRACECMD_CLOCK_UNKNOWN;\n}\n\n/**\n * tracecmd_clock_id2str - Convert clock ID to ftare clock name\n * @clock: Clock ID\n * Returns name of a ftrace clock\n */\nconst char *tracecmd_clock_id2str(enum tracecmd_clocks clock)\n{\n\tint i;\n\n\tfor (i = 0; trace_clocks[i].clock_str; i++) {\n\t\tif (trace_clocks[i].clock_id == clock)\n\t\t\treturn trace_clocks[i].clock_str;\n\t}\n\treturn NULL;\n}\n\n/**\n * tracecmd_set_debug - Set debug mode of the tracecmd library\n * @set_debug: The new \"debug\" mode. If true, the tracecmd library is\n * in \"debug\" mode\n */\nvoid tracecmd_set_debug(bool set_debug)\n{\n\tdebug = set_debug;\n\n\tif (set_debug)\n\t\ttracecmd_set_loglevel(TEP_LOG_DEBUG);\n\telse\n\t\ttracecmd_set_loglevel(TEP_LOG_CRITICAL);\n}\n\n/**\n * tracecmd_get_debug - Get debug mode of tracecmd library\n * Returns true, if the tracecmd library is in debug mode.\n *\n */\nbool tracecmd_get_debug(void)\n{\n\treturn debug;\n}\n\n/**\n * tracecmd_set_notimeout - Do not timeout waiting for responses\n * @set_notimeout: True or false to set notimeout mode.\n *\n * If @set_notimeout is true, then the library will not fail waiting for\n * responses. This is useful when running the code under gdb.\n * Note, if debug is set, then this makes no difference as it will always\n * not timeout.\n */\nvoid tracecmd_set_notimeout(bool set_notimeout)\n{\n\tnotimeout = set_notimeout;\n}\n\n/**\n * tracecmd_get_notimeout - Get setting of notimeout of tracecmd library\n * Returns true, if the tracecmd library has notimeout set.\n *\n */\nbool tracecmd_get_notimeout(void)\n{\n\treturn notimeout || debug;\n}\n\nvoid tracecmd_parse_proc_kallsyms(struct tep_handle *pevent,\n\t\t\t char *file, unsigned int size __maybe_unused)\n{\n\tunsigned long long addr;\n\tint sav_errno;\n\tchar *func;\n\tchar *line;\n\tchar *next = NULL;\n\tchar *mod;\n\tchar ch;\n\n\tline = strtok_r(file, \"\\n\", &next);\n\twhile (line) {\n\t\tint func_start, func_end = 0;\n\t\tint mod_start, mod_end = 0;\n\t\tint n;\n\n\t\tmod = NULL;\n\t\tsav_errno = errno;\n\t\terrno = 0;\n\t\tn = sscanf(line, \"%16llx %c %n%*s%n%*1[\\t][%n%*s%n\",\n\t\t\t   &addr, &ch, &func_start, &func_end, &mod_start, &mod_end);\n\t\tif (errno)\n\t\t\treturn;\n\t\terrno = sav_errno;\n\n\t\tif (n != 2 || !func_end)\n\t\t\treturn;\n\n\t\tfunc = line + func_start;\n\t\t/*\n\t\t * Hacks for\n\t\t *  - arm arch that adds a lot of bogus '$a' functions\n\t\t *  - x86-64 that reports per-cpu variable offsets as absolute\n\t\t */\n\t\tif (func[0] != '$' && ch != 'A' && ch != 'a') {\n\t\t\tline[func_end] = 0;\n\t\t\tif (mod_end) {\n\t\t\t\tmod = line + mod_start;\n\t\t\t\t/* truncate the extra ']' */\n\t\t\t\tline[mod_end - 1] = 0;\n\t\t\t}\n\t\t\ttep_register_function(pevent, func, addr, mod);\n\t\t}\n\n\t\tline = strtok_r(NULL, \"\\n\", &next);\n\t}\n}\n\nvoid tracecmd_parse_ftrace_printk(struct tep_handle *pevent,\n\t\t\t char *file, unsigned int size __maybe_unused)\n{\n\tunsigned long long addr;\n\tchar *printk;\n\tchar *line;\n\tchar *next = NULL;\n\tchar *addr_str;\n\tchar *fmt;\n\n\tline = strtok_r(file, \"\\n\", &next);\n\twhile (line) {\n\t\taddr_str = strtok_r(line, \":\", &fmt);\n\t\tif (!addr_str) {\n\t\t\ttracecmd_warning(\"printk format with empty entry\");\n\t\t\tbreak;\n\t\t}\n\t\taddr = strtoull(addr_str, NULL, 16);\n\t\t/* fmt still has a space, skip it */\n\t\tprintk = strdup(fmt+1);\n\t\tline = strtok_r(NULL, \"\\n\", &next);\n\t\ttep_register_print_string(pevent, printk, addr);\n\t\tfree(printk);\n\t}\n}\n\n/**\n * tracecmd_add_id - add an int to the event id list\n * @list: list to add the id to\n * @id: id to add\n * @len: current length of list of ids.\n *\n * The typical usage is:\n *\n *    events = tracecmd_add_id(events, id, len++);\n *\n * Returns the new allocated list with the id included.\n * the list will contain a '-1' at the end.\n *\n * The returned list should be freed with free().\n */\nint *tracecmd_add_id(int *list, int id, int len)\n{\n\tif (!list)\n\t\tlist = malloc(sizeof(*list) * 2);\n\telse\n\t\tlist = realloc(list, sizeof(*list) * (len + 2));\n\tif (!list)\n\t\treturn NULL;\n\n\tlist[len++] = id;\n\tlist[len] = -1;\n\n\treturn list;\n}\n\nstruct add_plugin_data {\n\tint ret;\n\tint index;\n\tchar **files;\n};\n\nstatic void add_plugin_file(struct tep_handle *pevent, const char *path,\n\t\t\t   const char *name, void *data)\n{\n\tstruct add_plugin_data *pdata = data;\n\tchar **ptr;\n\tint size;\n\tint i;\n\n\tif (pdata->ret)\n\t\treturn;\n\n\tsize = pdata->index + 2;\n\tptr = realloc(pdata->files, sizeof(char *) * size);\n\tif (!ptr)\n\t\tgoto out_free;\n\n\tpdata->files = ptr;\n\tptr[pdata->index] = strdup(name);\n\tif (!ptr[pdata->index])\n\t\tgoto out_free;\n\n\tpdata->index++;\n\tpdata->files[pdata->index] = NULL;\n\treturn;\n\n out_free:\n\tfor (i = 0; i < pdata->index; i++)\n\t\tfree(pdata->files[i]);\n\tfree(pdata->files);\n\tpdata->files = NULL;\n\tpdata->ret = errno;\n}\n\n/**\n * tcmd_util_find_plugin_files - find list of possible plugin files\n * @suffix: The suffix of the plugin files to find\n *\n * Searches the plugin directory for files that end in @suffix, and\n * will return an allocated array of file names, or NULL if none is\n * found.\n *\n * Must check against TRACECMD_ISERR(ret) as if an error happens\n * the errno will be returned with the TRACECMD_ERR_MSK to denote\n * such an error occurred.\n *\n * Use tcmd_util_free_plugin_files() to free the result.\n */\n__hidden char **tcmd_util_find_plugin_files(const char *suffix)\n{\n\tstruct add_plugin_data pdata;\n\n\tmemset(&pdata, 0, sizeof(pdata));\n\n\ttep_load_plugins_hook(NULL, suffix, add_plugin_file, &pdata);\n\n\tif (pdata.ret)\n\t\treturn TRACECMD_ERROR(pdata.ret);\n\n\treturn pdata.files;\n}\n\n/**\n * tcmd_util_free_plugin_files - free the result of tcmd_util_find_plugin_files()\n * @files: The result from tcmd_util_find_plugin_files()\n *\n * Frees the contents that were allocated by tcmd_util_find_plugin_files().\n */\nvoid __hidden tcmd_util_free_plugin_files(char **files)\n{\n\tint i;\n\n\tif (!files || TRACECMD_ISERR(files))\n\t\treturn;\n\n\tfor (i = 0; files[i]; i++) {\n\t\tfree(files[i]);\n\t}\n\tfree(files);\n}\n\nstatic char *get_source_plugins_dir(void)\n{\n\tchar *p, path[PATH_MAX+1];\n\tint ret;\n\n\tret = readlink(\"/proc/self/exe\", path, PATH_MAX);\n\tif (ret > PATH_MAX || ret < 0)\n\t\treturn NULL;\n\n\tpath[ret] = 0;\n\tdirname(path);\n\tp = strrchr(path, '/');\n\tif (!p)\n\t\treturn NULL;\n\t/* Check if we are in the the source tree */\n\tif (strcmp(p, \"/tracecmd\") != 0)\n\t\treturn NULL;\n\n\tstrcpy(p, \"/lib/traceevent/plugins\");\n\treturn strdup(path);\n}\n\n__hidden struct tep_plugin_list *\ntcmd_load_plugins(struct tep_handle *tep, int flags)\n{\n\tstruct tep_plugin_list *list;\n\tchar *path;\n\n\tif (flags & TRACECMD_FL_LOAD_NO_PLUGINS)\n\t\ttep_set_flag(tep, TEP_DISABLE_PLUGINS);\n\tif (flags & TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS)\n\t\ttep_set_flag(tep, TEP_DISABLE_SYS_PLUGINS);\n\n\tpath = get_source_plugins_dir();\n\tif (path)\n\t\ttep_add_plugin_path(tep, path, TEP_PLUGIN_LAST);\n\tfree(path);\n\n\tlist = tep_load_plugins(tep);\n\n\treturn list;\n}\n\n/**\n * tracecmd_set_loglevel - set log level of the library\n * @level: desired level of the library messages\n */\nvoid tracecmd_set_loglevel(enum tep_loglevel level)\n{\n\tlog_level = level;\n}\n\nvoid __weak tracecmd_warning(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (log_level < TEP_LOG_WARNING)\n\t\treturn;\n\n\tva_start(ap, fmt);\n\ttep_vprint(\"libtracecmd\", TEP_LOG_WARNING, true, fmt, ap);\n\tva_end(ap);\n}\n\nvoid __weak tracecmd_info(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (log_level < TEP_LOG_INFO)\n\t\treturn;\n\n\tva_start(ap, fmt);\n\ttep_vprint(\"libtracecmd\", TEP_LOG_INFO, false, fmt, ap);\n\tva_end(ap);\n}\n\nvoid __weak tracecmd_critical(const char *fmt, ...)\n{\n\tint ret;\n\tva_list ap;\n\n\tif (log_level < TEP_LOG_CRITICAL)\n\t\treturn;\n\n\tva_start(ap, fmt);\n\tret = tep_vprint(\"libtracecmd\", TEP_LOG_CRITICAL, true, fmt, ap);\n\tva_end(ap);\n\n\tif (debug) {\n\t\tif (!ret)\n\t\t\tret = -1;\n\t\texit(ret);\n\t}\n}\n\nvoid __weak tracecmd_debug(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!tracecmd_get_debug())\n\t\treturn;\n\n\tva_start(ap, fmt);\n\tvprintf(fmt, ap);\n\tva_end(ap);\n}\n\n#define LOG_BUF_SIZE 1024\nstatic void __plog(const char *prefix, const char *fmt, va_list ap, FILE *fp)\n{\n\tchar buf[LOG_BUF_SIZE];\n\tint r;\n\n\tr = vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);\n\n\tif (r > LOG_BUF_SIZE)\n\t\tr = LOG_BUF_SIZE;\n\n\tif (logfp) {\n\t\tfprintf(logfp, \"[%d]%s%.*s\", getpid(), prefix, r, buf);\n\t\tfflush(logfp);\n\t\treturn;\n\t}\n\n\tfprintf(fp, \"%.*s\", r, buf);\n}\n\nvoid tracecmd_plog(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tva_start(ap, fmt);\n\t__plog(\"\", fmt, ap, stdout);\n\tva_end(ap);\n\t/* Make sure it gets to the screen, in case we crash afterward */\n\tfflush(stdout);\n}\n\nvoid tracecmd_plog_error(const char *fmt, ...)\n{\n\tva_list ap;\n\tchar *str = \"\";\n\n\tva_start(ap, fmt);\n\t__plog(\"Error: \", fmt, ap, stderr);\n\tva_end(ap);\n\tif (errno)\n\t\tstr = strerror(errno);\n\tif (logfp)\n\t\tfprintf(logfp, \"\\n%s\\n\", str);\n\telse\n\t\tfprintf(stderr, \"\\n%s\\n\", str);\n}\n\n/**\n * tracecmd_set_logfile - Set file for logging\n * @logfile: Name of the log file\n *\n * Returns 0 on successful completion or -1 in case of error\n */\nint tracecmd_set_logfile(char *logfile)\n{\n\tif (logfp)\n\t\tfclose(logfp);\n\tlogfp = fopen(logfile, \"w\");\n\tif (!logfp)\n\t\treturn -1;\n\treturn 0;\n}\n\n/**\n * tracecmd_stack_tracer_status - Check stack trace status\n * @status: Returned stack trace status:\n *             0 - not configured, disabled\n *             non 0 - enabled\n *\n * Returns -1 in case of an error, 0 if file does not exist\n * (stack tracer not configured in kernel) or 1 on successful completion.\n */\nint tracecmd_stack_tracer_status(int *status)\n{\n\tstruct stat stat_buf;\n\tchar buf[64];\n\tlong num;\n\tint fd;\n\tint n;\n\n\tif (stat(PROC_STACK_FILE, &stat_buf) < 0) {\n\t\t/* stack tracer not configured on running kernel */\n\t\t*status = 0; /* not configured means disabled */\n\t\treturn 0;\n\t}\n\n\tfd = open(PROC_STACK_FILE, O_RDONLY);\n\n\tif (fd < 0)\n\t\treturn -1;\n\n\tn = read(fd, buf, sizeof(buf));\n\tclose(fd);\n\n\tif (n <= 0)\n\t\treturn -1;\n\n\tif (n >= sizeof(buf))\n\t\treturn -1;\n\n\tbuf[n] = 0;\n\n\terrno = 0;\n\tnum = strtol(buf, NULL, 10);\n\n\t/* Check for various possible errors */\n\tif (num > INT_MAX || num < INT_MIN || (!num && errno))\n\t\treturn -1;\n\n\t*status = num;\n\treturn 1; /* full success */\n}\n\n/**\n * tracecmd_count_cpus - Get the number of CPUs in the system\n *\n * Returns the number of CPUs in the system, or 0 in case of an error\n */\nint tracecmd_count_cpus(void)\n{\n\tstatic int once;\n\tchar buf[1024];\n\tint cpus = 0;\n\tchar *pbuf;\n\tsize_t *pn;\n\tFILE *fp;\n\tsize_t n;\n\tint r;\n\n\tcpus = sysconf(_SC_NPROCESSORS_CONF);\n\tif (cpus > 0)\n\t\treturn cpus;\n\n\tif (!once) {\n\t\tonce++;\n\t\ttracecmd_warning(\"sysconf could not determine number of CPUS\");\n\t}\n\n\t/* Do the hack to figure out # of CPUS */\n\tn = 1024;\n\tpn = &n;\n\tpbuf = buf;\n\n\tfp = fopen(\"/proc/cpuinfo\", \"r\");\n\tif (!fp) {\n\t\ttracecmd_critical(\"Can not read cpuinfo\");\n\t\treturn 0;\n\t}\n\n\twhile ((r = getline(&pbuf, pn, fp)) >= 0) {\n\t\tchar *p;\n\n\t\tif (strncmp(buf, \"processor\", 9) != 0)\n\t\t\tcontinue;\n\t\tfor (p = buf+9; isspace(*p); p++)\n\t\t\t;\n\t\tif (*p == ':')\n\t\t\tcpus++;\n\t}\n\tfclose(fp);\n\n\treturn cpus;\n}\n\n#define FNV_64_PRIME 0x100000001b3ULL\n/*\n * tracecmd_generate_traceid - Generate a unique ID, used to identify\n *\t\t\t       the current tracing session\n *\n * Returns unique ID\n */\nunsigned long long tracecmd_generate_traceid(void)\n{\n\tunsigned long long hash = 0;\n\tunsigned char *ustr;\n\tstruct sysinfo sinfo;\n\tstruct timespec ts;\n\tchar *str = NULL;\n\n\tclock_gettime(CLOCK_MONOTONIC_RAW, &ts);\n\tsysinfo(&sinfo);\n\tasprintf(&str, \"%ld %ld %ld %ld %ld %ld %ld %ld %d\",\n\t\t ts.tv_sec, ts.tv_nsec,\n\t\t sinfo.loads[0], sinfo.loads[1], sinfo.loads[2],\n\t\t sinfo.freeram, sinfo.sharedram, sinfo.freeswap,\n\t\t sinfo.procs);\n\tif (!str)\n\t\treturn 0;\n\tustr = (unsigned char *)str;\n\thash = 0;\n\twhile (*ustr) {\n\t\thash ^= (unsigned long long)*ustr++;\n\t\thash *= FNV_64_PRIME;\n\t}\n\n\tfree(str);\n\treturn hash;\n}\n\n/*\n * tracecmd_default_file_version - Get default trace file version of the library\n *\n * Returns the default trace file version\n */\nint tracecmd_default_file_version(void)\n{\n\treturn FILE_VERSION_DEFAULT;\n}\n\nbool tracecmd_is_version_supported(unsigned int version)\n{\n\tif (version <= FILE_VERSION_MAX)\n\t\treturn true;\n\treturn false;\n}\n\nstatic void __attribute__ ((constructor)) tracecmd_lib_init(void)\n{\n\ttracecmd_compress_init();\n}\n\nstatic void __attribute__((destructor)) tracecmd_lib_free(void)\n{\n\ttracecmd_compress_free();\n}\n\n__hidden bool tcmd_check_file_state(unsigned long file_version, int current_state, int new_state)\n{\n\tif (file_version >= FILE_VERSION_SECTIONS) {\n\t\tif (current_state < TRACECMD_FILE_INIT)\n\t\t\treturn false;\n\n\t\treturn true;\n\t}\n\n\tswitch (new_state) {\n\tcase TRACECMD_FILE_HEADERS:\n\tcase TRACECMD_FILE_FTRACE_EVENTS:\n\tcase TRACECMD_FILE_ALL_EVENTS:\n\tcase TRACECMD_FILE_KALLSYMS:\n\tcase TRACECMD_FILE_PRINTK:\n\tcase TRACECMD_FILE_CMD_LINES:\n\tcase TRACECMD_FILE_CPU_COUNT:\n\t\tif (current_state == (new_state - 1))\n\t\t\treturn true;\n\t\tbreak;\n\tcase TRACECMD_FILE_OPTIONS:\n\t\tif (file_version < FILE_VERSION_SECTIONS && current_state == TRACECMD_FILE_CPU_COUNT)\n\t\t\treturn true;\n\t\tbreak;\n\tcase TRACECMD_FILE_CPU_LATENCY:\n\tcase TRACECMD_FILE_CPU_FLYRECORD:\n\t\tif (current_state == TRACECMD_FILE_OPTIONS)\n\t\t\treturn true;\n\t\tbreak;\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "libtracecmd.pc.template",
    "content": "prefix=INSTALL_PREFIX\nlibdir=LIB_DIR\nincludedir=HEADER_DIR\n\nName: libtracecmd\nURL: https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/\nDescription: Library for creating and reading trace-cmd data files\nVersion: LIB_VERSION\nRequires: libtracefs >= LIBTRACEFS_MIN_VERSION\nCflags: -I${includedir}\nLibs: -L${libdir} -ltracecmd\n"
  },
  {
    "path": "make-trace-cmd.sh",
    "content": "#!/bin/bash\n\nif [ -z \"$INSTALL_PATH\" ]; then\n\techo\n\techo 'Error: No $INSTALL_PATH defined'\n\techo\n\techo \"   usage: [PREFIX=prefix][BUILD_PATH=/path/to/build][CFLAGS=custom-cflags] INSTALL_PATH=/path/to/install make-trace-cmd.sh install|install_libs|clean|uninstall\"\n\techo\n\techo \"     Used to create a self contained directory to copy to other machines.\"\n\techo\n\techo \"   Please read PACKAGING for more information.\"\n\techo\n\texit\nfi\n\nif [ ! -d $INSTALL_PATH ]; then\n\tmkdir $INSTALL_PATH\nfi\n\nif [ ! -z \"$BUILD_PATH\" ]; then\n\tif [ ! -d $BUILD_PATH ]; then\n\t\tmkdir $BUILD_PATH\n\tfi\n\tO_PATH=\"O=$BUILD_PATH\"\nfi\n\nif [ -z \"$PREFIX\" ]; then\n\tPREFIX=\"/usr\"\nfi\n\nPKG_PATH=`pkg-config --variable pc_path pkg-config | tr \":\" \" \" | cut -d' ' -f1`\n\nWITH_PATH=\"\"\n# If pkg-config supports --with-path, use that as well\nif pkg-config --with-path=/tmp --variable pc_path pkg-config &> /dev/null ; then\n\tWITH_PATH=\"--with-path=$INSTALL_PATH$PKG_PATH\"\nfi\n\nif [ -z \"$CFLAGS\" ]; then\n    CFLAGS=\"-g -Wall\"\nfi\n\nPKG_CONFIG_PATH=\"$INSTALL_PATH/$PKG_PATH\" PKG_CONFIG=\"pkg-config $WITH_PATH --define-variable=prefix=$INSTALL_PATH/$PREFIX\" CFLAGS=\"-I$INSTALL_PATH/$PREFIX/include $CFLAGS\" make DESTDIR=$INSTALL_PATH  $O_PATH prefix=$PREFIX $@\n"
  },
  {
    "path": "meson-vcs-tag.sh",
    "content": "#!/usr/bin/env bash\n# SPDX-License-Identifier: LGPL-2.1-or-later\n\nset -eu\nset -o pipefail\n\ndir=\"${1:?}\"\nfallback=\"${2:?}\"\n\n# Apparently git describe has a bug where it always considers the work-tree\n# dirty when invoked with --git-dir (even though 'git status' is happy). Work\n# around this issue by cd-ing to the source directory.\ncd \"$dir\"\n# Check that we have either .git/ (a normal clone) or a .git file (a work-tree)\n# and that we don't get confused if a tarball is extracted in a higher-level\n# git repository.\n[ -e .git ] && git describe --abbrev=7 --dirty=+ 2>/dev/null | sed 's/^v//' || echo \"$fallback\"\n"
  },
  {
    "path": "meson.build",
    "content": "# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nproject(\n    'trace-cmd', ['c'],\n    meson_version: '>= 0.50.0',\n    license: 'GPL-2.0',\n    version: '3.4.0',\n    default_options: [\n        'c_std=gnu99',\n        'buildtype=debug',\n        'default_library=both',\n        'prefix=/usr/local',\n        'warning_level=1'])\n\ncc = meson.get_compiler('c')\n\nprefixdir = get_option('prefix')\ndatadir = join_paths(prefixdir, get_option('datadir'))\nbindir = join_paths(prefixdir, get_option('bindir'))\nmandir = join_paths(prefixdir, get_option('mandir'))\nhtmldir = join_paths(prefixdir, get_option('htmldir'))\n\nconf = configuration_data()\n\nlibtraceevent_dep = dependency('libtraceevent', version: '>= 1.9.0', required: true)\nlibtracefs_dep = dependency('libtracefs', version: '>= 1.8.0', required: true)\n\nthreads_dep = dependency('threads', required: true)\ndl_dep = cc.find_library('dl', required : false)\n\nzlib_dep = dependency('zlib', required: false)\nconf.set('HAVE_ZLIB', zlib_dep.found(), description: 'Is zlib avialable?')\n\nlibzstd_dep = dependency('libzstd', version: '>= 1.4.0', required: false)\nconf.set('HAVE_ZSTD', libzstd_dep.found(), description: 'Is libzstd available?')\n\ncunit_dep = dependency('cunit', required : false)\n\nvsock_defined = get_option('vsock') and cc.has_header('linux/vm_sockets.h')\nconf.set('VSOCK', vsock_defined, description: 'Is vsock available?')\n\nperf_defined = cc.has_header('linux/perf_event.h')\nconf.set('PERF', perf_defined, description: 'Is perf available?')\n\nhave_ptrace = get_option('ptrace') and cc.compiles(\n    '''\n    #include <stdio.h>\n    #include <sys/ptrace.h>\n\n    int main (void)\n    {\n            int ret;\n            ret = ptrace(PTRACE_ATTACH, 0, NULL, 0);\n            ptrace(PTRACE_TRACEME, 0, NULL, 0);\n            ptrace(PTRACE_GETSIGINFO, 0, NULL, NULL);\n            ptrace(PTRACE_GETEVENTMSG, 0, NULL, NULL);\n            ptrace(PTRACE_SETOPTIONS, NULL, NULL,\n                           PTRACE_O_TRACEFORK |\n                           PTRACE_O_TRACEVFORK |\n                           PTRACE_O_TRACECLONE |\n                           PTRACE_O_TRACEEXIT);\n            ptrace(PTRACE_CONT, NULL, NULL, 0);\n            ptrace(PTRACE_DETACH, 0, NULL, NULL);\n            ptrace(PTRACE_SETOPTIONS, 0, NULL,\n                   PTRACE_O_TRACEFORK |\n                   PTRACE_O_TRACEVFORK |\n                   PTRACE_O_TRACECLONE |\n                   PTRACE_O_TRACEEXIT);\n            return ret;\n    }\n    ''',\n    name: 'ptrace')\nif not have_ptrace\n    conf.set10('NO_PTRACE', true, description: 'Is ptrace missing?')\n    conf.set('WARN_NO_PTRACE', true, description: 'Issue no ptrace warning?')\nendif\n\naudit_dep = dependency('audit', required: false)\nif not audit_dep.found()\n    conf.set10('NO_AUDIT', true, description: 'Is audit missing?')\n    conf.set('WARN_NO_AUDIT', true, description: 'Issue no audit warning?')\nendif\n\nconfig_h = configure_file(\n    output: 'config.h',\n    configuration: conf\n)\n\nversion = meson.project_version().split('.')\n\nvconf = configuration_data()\nvconf.set('VERSION_CODE', version[0].to_int() * 256 + version[1].to_int())\nvconf.set('EXTRAVERSION', '\"@0@\"'.format(version[2]))\nvconf.set('FILE_VERSION', '\"\"')\nvconf.set('VERSION_STRING', '\"@0@\"'.format(meson.project_version()))\n\nversion_tag = get_option('version-tag')\nif version_tag != ''\n    vconf.set('VERSION_GIT', '\"@0@\"'.format(version_tag))\nelse\n    r = run_command(\n        'meson-vcs-tag.sh',\n        meson.current_source_dir(),\n        meson.project_version(),\n        check: true)\n    vconf.set('VERSION_GIT', '\"@0@\"'.format(r.stdout().strip()))\nendif\nversion_h = configure_file(\n    output: 'tc_version.h',\n    configuration: vconf)\n\nadd_project_arguments(\n    [\n      '-D_GNU_SOURCE',\n      '-include', 'config.h',\n    ],\n    language : 'c')\n\nincdir = include_directories(['.', 'include'])\n\n# libtracecmd: trace-cmd currently depends on a statically linked\n# libtracecmd.  libtracecmd is sill very strongly coupled with\n# trace-cmd (or the other way around). To reduce the development setup\n# complexity we add some of the 'top meson.build' from libtracecmd and\n# make it simpler to use.\nlibrary_version = '1.5.1'\nlibtracecmd_standalone_build = false\nlibtracecmd_ext_incdir = include_directories(\n    [\n        'include',\n        'include/trace-cmd',\n        'tracecmd/include'\n    ])\nsubdir('lib/trace-cmd/include')\nsubdir('lib/trace-cmd/include/private')\nsubdir('lib/trace-cmd')\n\n# trace-cmd\nsubdir('tracecmd')\nsubdir('python')\nif get_option('utest') and cunit_dep.found()\n    subdir('utest')\nendif\n\nif get_option('doc')\nsubdir('Documentation/trace-cmd')\n\ncustom_target(\n    'docs',\n    output: 'docs',\n    depends: [html, man],\n    command: ['echo'])\nendif\n"
  },
  {
    "path": "meson_options.txt",
    "content": "# -*- mode: meson -*-\n# SPDX-License-Identifier: GPL-2.0\n\noption('version-tag', type : 'string',\n       description : 'override the git version string')\noption('vsock', type : 'boolean', value : true,\n       description : 'build with vsock support')\noption('ptrace', type : 'boolean', value : true,\n       description : 'build with ptrace support')\noption('htmldir', type : 'string', value : 'share/doc/trace-cmd-doc',\n       description : 'directory for HTML documentation')\noption('asciidoctor', type : 'boolean', value: false,\n       description : 'use asciidoctor instead of asciidoc')\noption('docbook-xls-172', type : 'boolean', value : false,\n       description : 'enable docbook XLS 172 workaround')\noption('asciidoc-no-roff', type : 'boolean', value : false,\n       description : 'enable no roff workaround')\noption('man-bold-literal', type : 'boolean', value : false,\n       description : 'enable bold literals')\noption('docbook-suppress-sp', type : 'boolean', value : false,\n       description : 'docbook suppress sp')\noption('python', type : 'combo', choices : ['auto', 'true', 'false'],\n       description : 'Generate trac-cmd Python bindings')\noption('doc', type : 'boolean', value: true,\n       description : 'produce documentation')\noption('utest', type : 'boolean', value: true,\n       description : 'build utest')\n"
  },
  {
    "path": "python/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\ninclude $(src)/scripts/utils.mk\n\nifdef BUILD_PYTHON_WORKS\nPYTHON_SO_INSTALL := ctracecmd.install\nPYTHON_PY_PROGS := event-viewer.install\nPYTHON_PY_LIBS := tracecmd.install\nendif\n\nctracecmd.so: ctracecmd.i $(LIBTRACECMD_STATIC)\n\tswig -Wall -python -noproxy \\\n\t\t-I$(src)/include/trace-cmd -I$(src)/lib/trace-cmd/include/private \\\n\t\t$(LIBTRACEEVENT_CFLAGS) ctracecmd.i\n\t$(CC) -fpic -c $(CPPFLAGS) $(CFLAGS) $(PYTHON_INCLUDES)  ctracecmd_wrap.c\n\t$(CC) --shared $(LIBTRACECMD_STATIC) $(LDFLAGS) $(LIBZSTD_LDLAGS) $(ZLIB_LDLAGS) \\\n\t\tctracecmd_wrap.o -o ctracecmd.so $(TRACE_LIBS)\n\n$(PYTHON_SO_INSTALL): %.install : %.so force\n\t$(Q)$(call do_install_data,$<,$(python_dir_SQ))\n\n$(PYTHON_PY_PROGS): %.install : %.py force\n\t$(Q)$(call do_install,$<,$(python_dir_SQ))\n\n$(PYTHON_PY_LIBS): %.install : %.py force\n\t$(Q)$(call do_install_data,$<,$(python_dir_SQ))\n\ninstall_python: $(PYTHON_SO_INSTALL) $(PYTHON_PY_PROGS) $(PYTHON_PY_LIBS)\n\n\nclean:\n\t$(RM) *.a *.so *.o .*.d ctracecmd_wrap.*\n\nforce:\n.PHONY: clean force\n"
  },
  {
    "path": "python/ctracecmd.i",
    "content": "// tracecmd.i\n%module ctracecmd\n%include \"typemaps.i\"\n%include \"constraints.i\"\n\n%nodefaultctor record;\n%nodefaultdtor record;\n\n%apply Pointer NONNULL { struct tracecmd_input *handle };\n%apply Pointer NONNULL { struct tep_handle *pevent };\n%apply Pointer NONNULL { struct tep_format_field * };\n%apply unsigned long long *OUTPUT {unsigned long long *}\n%apply int *OUTPUT {int *}\n\n\n%{\n#include \"trace-cmd.h\"\n#include \"trace-cmd-private-python.h\"\n#include \"event-parse.h\"\n#include \"event-utils.h\"\n#include <Python.h>\n%}\n\n\n%typemap(in) PyObject *pyfunc {\n\tif (!PyCallable_Check($input)) {\n\t\tPyErr_SetString(PyExc_TypeError, \"Need a callable object!\");\n\t\treturn NULL;\n\t}\n\t$1 = $input;\n}\n\n%ignore python_callback;\n\n%inline %{\nstatic int python_callback(struct trace_seq *s,\n\t\t\t   struct tep_record *record,\n\t\t\t   struct tep_event *event,\n\t\t\t   void *context);\n\nstatic int skip_output = 0;\n\nstatic void py_supress_trace_output(void)\n{\n\tskip_output = 1;\n}\n\nvoid warning(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (skip_output)\n\t\treturn;\n\n\tva_start(ap, fmt);\n\ttep_vprint(\"tracecmd\", TEP_LOG_WARNING, true, fmt, ap);\n\tva_end(ap);\n}\n\nPyObject *convert_pevent(unsigned long pevent)\n{\n\tvoid *pev = (void *)pevent;\n\treturn SWIG_NewPointerObj(SWIG_as_voidptr(pev), SWIGTYPE_p_tep_handle, 0);\n}\n\nvoid py_pevent_register_event_handler(struct tep_handle *pevent, int id,\n\t\t\t\t      char *subsys, char *evname,\n\t\t\t\t      PyObject *pyfunc)\n{\n\tPy_INCREF(pyfunc);\n\ttep_register_event_handler(pevent, id, subsys, evname,\n\t\t\t\t   python_callback, pyfunc);\n}\n\nstatic PyObject *py_field_get_stack(struct tep_handle *pevent,\n\t\t\t\t    struct tep_record *record,\n\t\t\t\t    struct tep_event *event,\n\t\t\t\t    int long_size)\n{\n\tPyObject *list;\n\tstruct tep_format_field *field;\n\tvoid *data = record->data;\n\tconst char *func = NULL;\n\tunsigned long addr;\n\n\tfield = tep_find_any_field(event, \"caller\");\n\tif (!field) {\n\t\tPyErr_SetString(PyExc_TypeError,\n\t\t\t\t\"Event doesn't have caller field\");\n\t\treturn NULL;\n\t}\n\n\tlist = PyList_New(0);\n\n\tfor (data += field->offset; data < record->data + record->size;\n\t     data += long_size) {\n\t\taddr = tep_read_number(event->tep, data, long_size);\n\n\t\tif ((long_size == 8 && addr == (unsigned long long)-1) ||\n\t\t    ((int)addr == -1))\n\t\t\tbreak;\n\t\tfunc = tep_find_function(event->tep, addr);\n\t\tif (PyList_Append(list, PyUnicode_FromString(func))) {\n\t\t\tPy_DECREF(list);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\treturn list;\n}\n\n#if PY_MAJOR_VERSION >= 3\nstatic PyObject *fromMemory(void *buf, size_t len)\n{\n\t\treturn PyMemoryView_FromMemory(buf, len, PyBUF_READ);\n}\n#define PY_INT_AS_LONG PyLong_AsLong\n#else\nstatic PyObject *fromMemory(void *buf, size_t len)\n{\n\t\treturn PyBuffer_FromMemory(buf, len);\n}\n#define PY_INT_AS_LONG PyInt_AS_LONG\n#endif\n\n\n\nstatic PyObject *py_field_get_data(struct tep_format_field *f, struct tep_record *r)\n{\n\tif (!strncmp(f->type, \"__data_loc \", 11)) {\n\t\tunsigned long long val;\n\t\tint len, offset;\n\n\t\tif (tep_read_number_field(f, r->data, &val)) {\n\t\t\tPyErr_SetString(PyExc_TypeError,\n\t\t\t\t\t\"Field is not a valid number\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\t/*\n\t\t * The actual length of the dynamic array is stored\n\t\t * in the top half of the field, and the offset\n\t\t * is in the bottom half of the 32 bit field.\n\t\t */\n\t\toffset = val & 0xffff;\n\t\tlen = val >> 16;\n\n\t\treturn fromMemory(r->data + offset, len);\n\t}\n\n\treturn fromMemory(r->data + f->offset, f->size);\n}\n\nstatic PyObject *py_field_get_str(struct tep_format_field *f, struct tep_record *r)\n{\n\tif (!strncmp(f->type, \"__data_loc \", 11)) {\n\t\tunsigned long long val;\n\t\tint offset;\n\n\t\tif (tep_read_number_field(f, r->data, &val)) {\n\t\t\tPyErr_SetString(PyExc_TypeError,\n\t\t\t\t\t\"Field is not a valid number\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\t/*\n\t\t * The actual length of the dynamic array is stored\n\t\t * in the top half of the field, and the offset\n\t\t * is in the bottom half of the 32 bit field.\n\t\t */\n\t\toffset = val & 0xffff;\n\n\t\treturn PyUnicode_FromString((char *)r->data + offset);\n\t}\n\n\treturn PyUnicode_FromStringAndSize((char *)r->data + f->offset,\n\t\t\t\tstrnlen((char *)r->data + f->offset, f->size));\n}\n\nstatic PyObject *py_format_get_keys(struct tep_event *ef, bool common_keys)\n{\n\tPyObject *list;\n\tstruct tep_format_field *f;\n\n\tlist = PyList_New(0);\n\n\tfor (f = common_keys ? ef->format.common_fields : ef->format.fields; f; f = f->next) {\n\t\tif (PyList_Append(list, PyUnicode_FromString(f->name))) {\n\t\t\tPy_DECREF(list);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\treturn list;\n}\n%}\n\n\n%wrapper %{\nstatic int python_callback(struct trace_seq *s,\n\t\t\t   struct tep_record *record,\n\t\t\t   struct tep_event *event,\n\t\t\t   void *context)\n{\n\tPyObject *arglist, *result;\n\tint r = 0;\n\n\trecord->ref_count++;\n\n\targlist = Py_BuildValue(\"(OOO)\",\n\t\tSWIG_NewPointerObj(SWIG_as_voidptr(s),\n\t\t\t\t   SWIGTYPE_p_trace_seq, 0),\n\t\tSWIG_NewPointerObj(SWIG_as_voidptr(record),\n\t\t\t\t   SWIGTYPE_p_tep_record, 0),\n\t\tSWIG_NewPointerObj(SWIG_as_voidptr(event),\n\t\t\t\t   SWIGTYPE_p_tep_event, 0));\n\n\tresult = PyObject_Call(context, arglist, NULL);\n\tPy_XDECREF(arglist);\n\tif (result && result != Py_None) {\n\t\tif (!PyInt_Check(result)) {\n\t\t\tPyErr_SetString(PyExc_TypeError,\n\t\t\t\t\t\"callback must return int\");\n\t\t\tPyErr_Print();\n\t\t\tPy_XDECREF(result);\n\t\t\treturn 0;\n\t\t}\n\t\tr = PY_INT_AS_LONG(result);\n\t} else if (result == Py_None)\n\t\tr = 0;\n\telse\n\t\tPyErr_Print();\n\n\tPy_XDECREF(result);\n\n\treturn r;\n}\n%}\n\n\n%ignore trace_seq_vprintf;\n%ignore vpr_stat;\n%ignore tep_plugin_kvm_get_func;\n%ignore tep_plugin_kvm_put_func;\n\n/* SWIG can't grok these, define them to nothing */\n#define __trace\n#define __attribute__(x)\n#define __thread\n\n%include \"trace-cmd.h\"\n%include \"trace-cmd-private-python.h\"\n%include <trace-seq.h>\n%include <event-parse.h>\n"
  },
  {
    "path": "python/event-viewer.py",
    "content": "#!/usr/bin/env python2\n\nimport getopt\nfrom gobject import *\nimport gtk\nfrom tracecmd import *\nimport time\n\napp = None\ndata_func_cnt = 0\n\n# In a \"real\" app these width should be determined at runtime testing max length\n# strings in the current font.\nTS_COL_W    = 150\nCPU_COL_W   = 35\nEVENT_COL_W = 150\nPID_COL_W   = 75\nCOMM_COL_W  = 250\n\n\ndef timing(func):\n  def wrapper(*arg):\n      start = time.time()\n      ret = func(*arg)\n      end = time.time()\n      print('@%s took %0.3f s' % (func.func_name, (end-start)))\n      return ret\n  return wrapper\n\n\nclass EventStore(gtk.GenericTreeModel):\n    class EventRef(object):\n        '''Inner class to build the trace event index'''\n        def __init__(self, index, timestamp, offset, cpu):\n            self.index = index\n            self.offset = offset\n            self.ts = timestamp\n            self.cpu = cpu\n\n        def __cmp__(self, other):\n            if self.ts < other.ts:\n                return -1\n            if self.ts > other.ts:\n                return 1\n            if self.offset < other.offset:\n                return -1\n            if self.offset > other.offset:\n                return 1\n            return 0\n\n    # The store only returns the record offset into the trace\n    # The view is responsible for looking up the Event with the offset\n    column_types = (long,)\n\n    @timing\n    def __init__(self, trace):\n        gtk.GenericTreeModel.__init__(self)\n        self.trace = trace\n        self.refs = []\n        self._load_trace()\n        self._sort()\n        self._reindex()\n\n    @timing\n    def _load_trace(self):\n        print(\"Building trace index...\")\n        index = 0\n        for cpu in range(0, trace.cpus):\n            rec = tracecmd_read_data(self.trace._handle, cpu)\n            while rec:\n                offset = tep_record_offset_get(rec)\n                ts = tep_record_ts_get(rec)\n                self.refs.append(self.EventRef(index, ts, offset, cpu))\n                index = index + 1\n                rec = tracecmd_read_data(self.trace._handle, cpu)\n        print(\"Loaded %d events from trace\" % (index))\n\n    @timing\n    def _sort(self):\n        self.refs.sort()\n\n    @timing\n    def _reindex(self):\n        for i in range(0, len(self.refs)):\n            self.refs[i].index = i\n\n    def on_get_flags(self):\n        return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST\n\n    def on_get_n_columns(self):\n        return len(self.column_types)\n\n    def on_get_column_type(self, col):\n        return self.column_types[col]\n\n    def on_get_iter(self, path):\n        return self.refs[path[0]]\n\n    def on_get_path(self, ref):\n        return ref.index\n\n    def on_get_value(self, ref, col):\n        '''\n        The Event record was getting deleted when passed back via this\n        method, now it just returns the ref itself. Use get_event() instead.\n        '''\n        if col == 0:\n            #return self.trace.read_event_at(ref.offset)\n            return ref\n        return None\n\n    def on_iter_next(self, ref):\n        try:\n            return self.refs[ref.index+1]\n        except IndexError:\n            return None\n\n    def on_iter_children(self, ref):\n        if ref:\n            return None\n        return self.refs[0]\n\n    def on_iter_has_child(self, ref):\n        return False\n\n    def on_iter_n_children(self, ref):\n        if ref:\n            return 0\n        return len(self.refs)\n\n    def on_iter_nth_child(self, ref, n):\n        if ref:\n            return None\n        try:\n            return self.refs[n]\n        except IndexError:\n            return None\n\n    def on_iter_parent(self, child):\n        return None\n\n    def get_event(self, iter):\n        '''This allocates a record which must be freed by the caller'''\n        try:\n            ref = self.refs[self.get_path(iter)[0]]\n            ev = self.trace.read_event_at(ref.offset)\n            return ev\n        except IndexError:\n            return None\n\n\nclass EventView(gtk.TreeView):\n    def __init__(self, model):\n        gtk.TreeView.__init__(self, model)\n        self.set_fixed_height_mode(True)\n\n        ts_col = gtk.TreeViewColumn(\"Time (s)\")\n        ts_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        ts_col.set_fixed_width(TS_COL_W)\n        ts_cell = gtk.CellRendererText()\n        ts_col.pack_start(ts_cell, False)\n        ts_col.set_cell_data_func(ts_cell, self.data_func, \"ts\")\n        self.append_column(ts_col)\n\n        cpu_col = gtk.TreeViewColumn(\"CPU\")\n        cpu_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        cpu_col.set_fixed_width(CPU_COL_W)\n        cpu_cell = gtk.CellRendererText()\n        cpu_col.pack_start(cpu_cell, False)\n        cpu_col.set_cell_data_func(cpu_cell, self.data_func, \"cpu\")\n        self.append_column(cpu_col)\n\n        event_col = gtk.TreeViewColumn(\"Event\")\n        event_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        event_col.set_fixed_width(EVENT_COL_W)\n        event_cell = gtk.CellRendererText()\n        event_col.pack_start(event_cell, False)\n        event_col.set_cell_data_func(event_cell, self.data_func, \"event\")\n        self.append_column(event_col)\n\n        pid_col = gtk.TreeViewColumn(\"PID\")\n        pid_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        pid_col.set_fixed_width(PID_COL_W)\n        pid_cell = gtk.CellRendererText()\n        pid_col.pack_start(pid_cell, False)\n        pid_col.set_cell_data_func(pid_cell, self.data_func, \"pid\")\n        self.append_column(pid_col)\n\n        comm_col = gtk.TreeViewColumn(\"Comm\")\n        comm_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        comm_col.set_fixed_width(COMM_COL_W)\n        comm_cell = gtk.CellRendererText()\n        comm_col.pack_start(comm_cell, False)\n        comm_col.set_cell_data_func(comm_cell, self.data_func, \"comm\")\n        self.append_column(comm_col)\n\n    def data_func(self, col, cell, model, iter, data):\n        global app, data_func_cnt\n\n        ev = model.get_event(iter)\n        #ev = model.get_value(iter, 0)\n        if not ev:\n            return False\n\n        if data == \"ts\":\n            cell.set_property(\"markup\", \"%d.%09d\" % (ev.ts/1000000000,\n                                                     ev.ts%1000000000))\n            data_func_cnt = data_func_cnt + 1\n            if app:\n                app.inc_data_func()\n        elif data == \"cpu\":\n            cell.set_property(\"markup\", ev.cpu)\n        elif data == \"event\":\n            cell.set_property(\"markup\", ev.name)\n        elif data == \"pid\":\n            cell.set_property(\"markup\", ev.pid)\n        elif data == \"comm\":\n            cell.set_property(\"markup\", ev.comm)\n        else:\n            print(\"Unknown Column:\", data)\n            return False\n\n        return True\n\n\nclass EventViewerApp(gtk.Window):\n    def __init__(self, trace):\n        gtk.Window.__init__(self)\n\n        self.set_size_request(650, 400)\n        self.set_position(gtk.WIN_POS_CENTER)\n\n        self.connect(\"destroy\", gtk.main_quit)\n        self.set_title(\"Event Viewer\")\n\n        store = EventStore(trace)\n        view = EventView(store)\n\n        sw = gtk.ScrolledWindow()\n        sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)\n        sw.add(view)\n\n        # track how often the treeview data_func is called\n        self.data_func_label = gtk.Label(\"0\")\n        hbox = gtk.HBox()\n        hbox.pack_start(gtk.Label(\"TS Data Func Calls:\"), False, False)\n        hbox.pack_start(self.data_func_label, False, False)\n\n        vbox = gtk.VBox()\n        vbox.pack_start(hbox, False)\n        vbox.pack_end(sw)\n\n        self.add(vbox)\n        self.show_all()\n\n    def inc_data_func(self):\n        global data_func_cnt\n        self.data_func_label.set_text(str(data_func_cnt))\n\n\nif __name__ == \"__main__\":\n    if len(sys.argv) >=2:\n        filename = sys.argv[1]\n    else:\n        filename = \"trace.dat\"\n\n    print(\"Initializing trace...\")\n    trace = Trace(filename)\n    print(\"Initializing app...\")\n    app = EventViewerApp(trace)\n    print(\"Go!\")\n    gtk.main()\n"
  },
  {
    "path": "python/meson.build",
    "content": "# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nwant_python = get_option('python')\nif want_python != 'false'\n    python3 = import('python').find_installation('python3')\n    py3_dep = python3.dependency(required: want_python == 'true')\n    swig = find_program('swig', required: want_python == 'true')\n    header_found = cc.has_header('Python.h', dependencies: py3_dep)\n    have_python_support = py3_dep.found() and swig.found() and header_found\nelse\n    have_python_support = false\nendif\n\nif have_python_support\n    pymod_swig = custom_target(\n        'ctracecmd.py',\n        input:   ['ctracecmd.i'],\n        output:  ['ctracecmd.py', 'ctracecmd_wrap.c'],\n        command: [\n            swig,\n            '-python',\n            '-I' + meson.current_source_dir() + '/../include/trace-cmd',\n            '-I' + meson.current_source_dir() + '/../lib/trace-cmd/include/private',\n            '-I' + libtraceevent_dep.get_pkgconfig_variable('prefix') + '/include/traceevent',\n            '-o', '@OUTPUT1@',\n            '@INPUT0@'],\n        install: true,\n        install_dir: [ python3.get_install_dir(pure: false, subdir: 'trace-cmd'), false])\n\n    incdir_py = include_directories(['.', '../include/trace-cmd', '../lib/trace-cmd/include/private'])\n\n    pyctracecmd_clib = python3.extension_module(\n        '_ctracecmd',\n        pymod_swig[1],\n        dependencies : [libtraceevent_dep, libtracefs_dep, py3_dep],\n        include_directories: [incdir, incdir_py],\n        install: true,\n        subdir: 'trace-cmd')\nendif\n"
  },
  {
    "path": "python/tracecmd.py",
    "content": "#\n# Copyright (C) International Business Machines  Corp., 2009\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# 2009-Dec-17:\tInitial version by Darren Hart <dvhltc@us.ibm.com>\n#\n\nfrom functools import cached_property\nfrom collections.abc import Mapping\nfrom itertools import chain\nfrom ctracecmd import *\n\n\"\"\"\nPython interface to the tracecmd library for parsing ftrace traces\n\nPython tracecmd applications should be written to this interface. It will be\nupdated as the tracecmd C API changes and try to minimze the impact to python\napplications. The ctracecmd Python module is automatically generated using SWIG\nand it is recommended applications not use it directly.\n\nTODO: consider a complete class hierarchy of ftrace events...\n\"\"\"\n\nclass Event(Mapping):\n    \"\"\"\n    This class can be used to access event data\n    according to an event's record and format.\n    \"\"\"\n    def __init__(self, pevent, record, format):\n        self._pevent = pevent\n        self._record = record\n        self._format = format\n\n    def __str__(self):\n        return \"%d.%09d CPU%d %s: pid=%d comm=%s type=%d\" % \\\n               (self.ts/1000000000, self.ts%1000000000, self.cpu, self.name,\n                self.num_field(\"common_pid\"), self.comm, self.type)\n\n    def __del__(self):\n        tracecmd_free_record(self._record)\n\n    def __getitem__(self, n):\n        if n.startswith('common_'):\n            f = tep_find_common_field(self._format, n)\n        else:\n            f = tep_find_field(self._format, n)\n        if f is None:\n            raise KeyError(\"no field '%s'\" % n)\n        return Field(self._record, f)\n\n    def __iter__(self):\n        yield from chain(self.common_keys, self.keys)\n\n    def __len__(self):\n        return len(self.common_keys) + len(self.keys)\n\n    @cached_property\n    def common_keys(self):\n        return py_format_get_keys(self._format, True)\n\n    @cached_property\n    def keys(self):\n        return py_format_get_keys(self._format, False)\n\n    @cached_property\n    def comm(self):\n        return tep_data_comm_from_pid(self._pevent, self.pid)\n\n    @cached_property\n    def cpu(self):\n        return tep_record_cpu_get(self._record)\n\n    @cached_property\n    def name(self):\n        return tep_event_name_get(self._format)\n\n    @cached_property\n    def pid(self):\n        return tep_data_pid(self._pevent, self._record)\n\n    @cached_property\n    def ts(self):\n        return tep_record_ts_get(self._record)\n\n    @cached_property\n    def type(self):\n        return tep_data_type(self._pevent, self._record)\n\n    def num_field(self, name):\n        f = tep_find_any_field(self._format, name)\n        if f is None:\n            return None\n        ret, val = tep_read_number_field(f, tep_record_data_get(self._record))\n        if ret:\n            return None\n        return val\n\n    def str_field(self, name):\n        f = tep_find_any_field(self._format, name)\n        if f is None:\n            return None\n        return py_field_get_str(f, self._record)\n\n    def stack_field(self, long_size):\n        return py_field_get_stack(self._pevent, self._record, self._format,\n                                  long_size)\n\nclass TraceSeq(object):\n    def __init__(self, trace_seq):\n        self._trace_seq = trace_seq\n\n    def puts(self, s):\n        return trace_seq_puts(self._trace_seq, s)\n\nclass FieldError(Exception):\n    pass\n\nclass Field(object):\n    def __init__(self, record, field):\n        self._record = record\n        self._field = field\n\n    @cached_property\n    def data(self):\n        return py_field_get_data(self._field, self._record)\n\n    def __long__(self):\n        ret, val =  tep_read_number_field(self._field,\n                                          tep_record_data_get(self._record))\n        if ret:\n            raise FieldError(\"Not a number field\")\n        return val\n    __int__ = __long__\n\n    def __str__(self):\n        return py_field_get_str(self._field, self._record)\n\nclass PEvent(object):\n    def __init__(self, pevent):\n        self._pevent = pevent\n\n    def _handler(self, cb, s, record, event_fmt):\n        return cb(TraceSeq(s), Event(self._pevent, record, event_fmt))\n\n    def register_event_handler(self, subsys, event_name, callback):\n        l = lambda s, r, e: self._handler(callback, s, r, e)\n\n        py_pevent_register_event_handler(\n                  self._pevent, -1, subsys, event_name, l)\n\n    @cached_property\n    def file_endian(self):\n        if tep_is_file_bigendian(self._pevent):\n            return '>'\n        return '<'\n\n\nclass FileFormatError(Exception):\n    pass\n\nclass Trace(object):\n    \"\"\"\n    Trace object represents the trace file it is created with.\n\n    The Trace object aggregates the tracecmd structures and functions that are\n    used to manage the trace and extract events from it.\n    \"\"\"\n    def __init__(self, filename):\n        self._handle = tracecmd_open(filename, 0)\n        self._pevent = tracecmd_get_tep(self._handle)\n\n    @cached_property\n    def cpus(self):\n        return tracecmd_cpus(self._handle)\n\n    @cached_property\n    def long_size(self):\n        return tracecmd_long_size(self._handle)\n\n    def read_event(self, cpu):\n        rec = tracecmd_read_data(self._handle, cpu)\n        if rec:\n            type = tep_data_type(self._pevent, rec)\n            format = tep_find_event(self._pevent, type)\n            # rec ownership goes over to Event instance\n            return Event(self._pevent, rec, format)\n        return None\n\n    def read_event_at(self, offset):\n        res = tracecmd_read_at(self._handle, offset)\n        # SWIG only returns the CPU if the record is None for some reason\n        if isinstance(res, int):\n            return None\n        rec, cpu = res\n        type = tep_data_type(self._pevent, rec)\n        format = tep_find_event(self._pevent, type)\n        # rec ownership goes over to Event instance\n        return Event(self._pevent, rec, format)\n\n    def read_next_event(self):\n        res = tracecmd_read_next_data(self._handle)\n        if isinstance(res, int):\n            return None\n        rec, cpu = res\n        type = tep_data_type(self._pevent, rec)\n        format = tep_find_event(self._pevent, type)\n        return Event(self._pevent, rec, format)\n\n    def peek_event(self, cpu):\n        rec = tracecmd_peek_data_ref(self._handle, cpu)\n        if rec is None:\n            return None\n        type = tep_data_type(self._pevent, rec)\n        format = tep_find_event(self._pevent, type)\n        # rec ownership goes over to Event instance\n        return Event(self._pevent, rec, format)\n\n\n# Basic builtin test, execute module directly\nif __name__ == \"__main__\":\n    t = Trace(\"trace.dat\")\n    print(f\"Trace contains data for {t.cpus} cpus, long has {t.long_size} bytes\")\n\n    print(\"Peek the first event on CPU0\")\n    print(\"\\t%s\" % (t.peek_event(0)))\n\n    print(\"Events by CPUs\")\n    for cpu in range(0, t.cpus):\n        print(\"CPU %d\" % (cpu))\n        ev = t.read_event(cpu)\n        while ev:\n            print(\"\\t%s\" % (ev))\n            ev = t.read_event(cpu)\n\n    t = Trace(\"trace.dat\")\n\n    print(\"Events by time\")\n    ev = t.read_next_event()\n    while ev:\n        print(\"\\t%s\" % (ev))\n        ev = t.read_next_event()\n"
  },
  {
    "path": "python/tracecmdgui.py",
    "content": "#\n# Copyright (C) International Business Machines  Corp., 2009\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# 2009-Dec-31:\tInitial version by Darren Hart <dvhltc@us.ibm.com>\n#\n\nimport gobject #delete me ?\nimport time\nimport sys\nimport gtk\nfrom tracecmd import *\nfrom ctracecmdgui import *\n\n\"\"\"\nPython interface for tracecmd GTK widgets\n\nPython tracecmd applications should be written to this interface. It will be\nupdated as the tracecmd gui C API changes and try to minimze the impact to\npython applications. The ctracecmdgui Python module is automatically generated\nusing SWIG and it is recommended applications not use it directly.\n\"\"\"\n\n# In a \"real\" app these width should be determined at runtime testing max length\n# strings in the current font.\nTS_COL_W    = 150\nCPU_COL_W   = 35\nEVENT_COL_W = 150\nPID_COL_W   = 75\nCOMM_COL_W  = 250\n\n\ndef timing(func):\n  def wrapper(*arg):\n      start = time.time()\n      ret = func(*arg)\n      end = time.time()\n      print('@%s took %0.3f s' % (func.func_name, (end-start)))\n      return ret\n  return wrapper\n\n\nclass EventStore(gtk.GenericTreeModel):\n    # FIXME: get these from the C code: trace_view_store->column_types ...\n    @timing\n    def __init__(self, trace):\n        gtk.GenericTreeModel.__init__(self)\n        self.trace = trace\n        self.cstore = trace_view_store_new(trace.handle)\n        self.gtk_cstore = trace_view_store_as_gtk_tree_model(self.cstore)\n        num_rows = trace_view_store_num_rows_get(self.cstore)\n        print(\"Loaded %d events from trace\" % (num_rows))\n\n    def on_get_flags(self):\n        return trace_view_store_get_flags(self.gtk_cstore)\n\n    def on_get_n_columns(self):\n        return trace_view_store_get_n_columns(self.gtk_cstore)\n\n    def on_get_column_type(self, col):\n        # I couldn't figure out how to convert the C GType into the python\n        # GType. The current typemap converts the C GType into the python type,\n        # which is what this function is supposed to return anyway.\n        pytype = trace_view_store_get_column_type(self.gtk_cstore, col)\n        return pytype\n\n    def on_get_iter(self, path):\n        if len(path) > 1 and path[1] != 1:\n            return None\n        n = path[0]\n        rec = trace_view_store_get_row(self.cstore, n)\n        return rec\n\n    def on_get_path(self, rec):\n        if not rec:\n            return None\n        start_row = trace_view_store_start_row_get(self.cstore)\n        return (trace_view_record_pos_get(rec) - start_row,)\n\n    def on_get_value(self, rec, col):\n        # FIXME: write SWIG wrapper to marshal the Gvalue and wrap the rec in an\n        # Iter\n        pass\n        #return trace_view_store_get_value_py(self.cstore, rec, col)\n\n    def on_iter_next(self, rec):\n        pos = trace_view_record_pos_get(rec)\n        start_row = trace_view_store_start_row_get(self.cstore)\n        return trace_view_store_get_row(self.cstore, pos - start_row + 1)\n\n    def on_iter_children(self, rec):\n        if rec:\n            return None\n        return trace_view_store_get_row(self.cstore, 0)\n\n    def on_iter_has_child(self, rec):\n        return False\n\n    def on_iter_n_children(self, rec):\n        if rec:\n            return 0\n        return trace_view_store_num_rows_get(self.cstore)\n\n    def on_iter_nth_child(self, rec, n):\n        if rec:\n            return None\n        return trace_view_store_get_row(self.cstore, n)\n\n    def on_iter_parent(self, child):\n        return None\n\n    def get_event(self, iter):\n        path = self.get_path(iter)\n        if not path:\n            return None\n        rec = trace_view_store_get_row(self.cstore, path[0])\n        if not rec:\n            return None\n        ev = self.trace.read_event_at(trace_view_record_offset_get(rec))\n        return ev\n\n\nclass EventView(gtk.TreeView):\n    def __init__(self, model):\n        gtk.TreeView.__init__(self, model)\n        self.set_fixed_height_mode(True)\n\n        ts_col = gtk.TreeViewColumn(\"Time (s)\")\n        ts_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        ts_col.set_fixed_width(TS_COL_W)\n        ts_cell = gtk.CellRendererText()\n        ts_col.pack_start(ts_cell, False)\n        ts_col.set_cell_data_func(ts_cell, self.data_func, \"ts\")\n        self.append_column(ts_col)\n\n        cpu_col = gtk.TreeViewColumn(\"CPU\")\n        cpu_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        cpu_col.set_fixed_width(CPU_COL_W)\n        cpu_cell = gtk.CellRendererText()\n        cpu_col.pack_start(cpu_cell, False)\n        cpu_col.set_cell_data_func(cpu_cell, self.data_func, \"cpu\")\n        self.append_column(cpu_col)\n\n        event_col = gtk.TreeViewColumn(\"Event\")\n        event_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        event_col.set_fixed_width(EVENT_COL_W)\n        event_cell = gtk.CellRendererText()\n        event_col.pack_start(event_cell, False)\n        event_col.set_cell_data_func(event_cell, self.data_func, \"event\")\n        self.append_column(event_col)\n\n        pid_col = gtk.TreeViewColumn(\"PID\")\n        pid_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        pid_col.set_fixed_width(PID_COL_W)\n        pid_cell = gtk.CellRendererText()\n        pid_col.pack_start(pid_cell, False)\n        pid_col.set_cell_data_func(pid_cell, self.data_func, \"pid\")\n        self.append_column(pid_col)\n\n        comm_col = gtk.TreeViewColumn(\"Comm\")\n        comm_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)\n        comm_col.set_fixed_width(COMM_COL_W)\n        comm_cell = gtk.CellRendererText()\n        comm_col.pack_start(comm_cell, False)\n        comm_col.set_cell_data_func(comm_cell, self.data_func, \"comm\")\n        self.append_column(comm_col)\n\n    def data_func(self, col, cell, model, iter, data):\n        ev = model.get_event(iter)\n        #ev = model.get_value(iter, 0)\n        if not ev:\n            return False\n\n        if data == \"ts\":\n            cell.set_property(\"markup\", \"%d.%d\" % (ev.ts/1000000000,\n                                                   ev.ts%1000000000))\n        elif data == \"cpu\":\n            cell.set_property(\"markup\", ev.cpu)\n        elif data == \"event\":\n            cell.set_property(\"markup\", ev.name)\n        elif data == \"pid\":\n            cell.set_property(\"markup\", ev.pid)\n        elif data == \"comm\":\n            cell.set_property(\"markup\", ev.comm)\n        else:\n            print(\"Unknown Column:\", data)\n            return False\n\n        return True\n\n\nclass EventViewerApp(gtk.Window):\n    def __init__(self, trace):\n        gtk.Window.__init__(self)\n\n        self.set_size_request(650, 400)\n        self.set_position(gtk.WIN_POS_CENTER)\n\n        self.connect(\"destroy\", gtk.main_quit)\n        self.set_title(\"Event Viewer\")\n\n        store = EventStore(trace)\n        view = EventView(store)\n\n        sw = gtk.ScrolledWindow()\n        sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)\n        sw.add(view)\n\n        # track how often the treeview data_func is called\n        self.add(sw)\n        self.show_all()\n\n\n# Basic builtin test, execute module directly\nif __name__ == \"__main__\":\n    if len(sys.argv) >=2:\n        filename = sys.argv[1]\n    else:\n        filename = \"trace.dat\"\n\n    print(\"Initializing trace...\")\n    trace = Trace(filename)\n    print(\"Initializing app...\")\n    app = EventViewerApp(trace)\n    print(\"Go!\")\n    gtk.main()\n"
  },
  {
    "path": "scripts/debug/tsync_hist.py",
    "content": "# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2019, VMware Inc, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n# Copyright (C) 2019, VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>\n\n\nimport matplotlib.pyplot as plt\nimport matplotlib.lines as mlines\nimport numpy as np\nimport sys\n\ndef newline(p1, p2):\n    ax = plt.gca()\n    xmin, xmax = ax.get_xbound()\n\n    if(p2[0] == p1[0]):\n        xmin = xmax = p1[0]\n        ymin, ymax = ax.get_ybound()\n    else:\n        ymax = p1[1]+(p2[1]-p1[1])/(p2[0]-p1[0])*(xmax-p1[0])\n        ymin = p1[1]+(p2[1]-p1[1])/(p2[0]-p1[0])*(xmin-p1[0])\n\n    l = mlines.Line2D([xmin,xmax], [ymin,ymax], color='red')\n    ax.add_line(l)\n    return l\n\n\ndata = np.loadtxt(fname = sys.argv[1])\nselected_ts  = data[-1, 1]\nselected_ofs = data[-1, 0]\ndata = data[:-1,:]\n\nx = data[:, 1] - data[:, 0]\n\nmean = x.mean()\nstd = x.std()\n\nnum_bins = 500\nmin = x.min() #+ .4 * (x.max() - x.min())\nmax = x.max() #- .4 * (x.max() - x.min())\nbins = np.linspace(min, max, num_bins, endpoint = False, dtype=int)\n\nfig, ax = plt.subplots()\n\n# the histogram of the data\nn, bins, patches = ax.hist(x, bins, histtype=u'step');\n\nax.set_xlabel('clock offset [$\\mu$s]')\nax.set_ylabel('entries')\nax.set_title(\"$\\sigma$=%i\" % std)\n\nx1, y1 = [selected_ofs, min], [selected_ofs, max]\nnewline(x1, y1)\n\n# Tweak spacing to prevent clipping of ylabel\nfig.tight_layout()\nplt.show()\n"
  },
  {
    "path": "scripts/debug/tsync_readme",
    "content": "PTP-like algorithm debug\n========================\n\ntsync_*.py scripts can be used to visualise debug files, written when the PTP-like algorithm\nis compiled with TSYNC_DEBUG defined. The files are located in the guest machine:\n    s-cid*.txt - For each offset calculation: host and guest clocks and calculated offset.\n    res-cid*.txt - For each tracing session: all calculated clock offsets.\n\ntsync_hist.py plots a histogram, using data from a s-cid*.txt file:\n\t\"python tsync_hist.py s-cid2_1.txt\"\ntsync_res.py plots a line, using data from res-cid*.txt file:\n\t\"python tsync_res.py res-cid2.txt\"\n"
  },
  {
    "path": "scripts/debug/tsync_res.py",
    "content": "# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2019, VMware Inc, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n# Copyright (C) 2019, VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>\n\n\nimport matplotlib.pyplot as plt\nimport matplotlib.lines as mlines\nimport numpy as np\nimport sys\n\ndef newline(p1, p2):\n    ax = plt.gca()\n    xmin, xmax = ax.get_xbound()\n\n    if(p2[0] == p1[0]):\n        xmin = xmax = p1[0]\n        ymin, ymax = ax.get_ybound()\n    else:\n        ymax = p1[1]+(p2[1]-p1[1])/(p2[0]-p1[0])*(xmax-p1[0])\n        ymin = p1[1]+(p2[1]-p1[1])/(p2[0]-p1[0])*(xmin-p1[0])\n\n    l = mlines.Line2D([xmin,xmax], [ymin,ymax], color='red')\n    ax.add_line(l)\n    return l\n\ndata = np.loadtxt(fname = sys.argv[1])\nx = data[:, 0]\ny = data[:, 1]\n\nfig, ax = plt.subplots()\n\nax.set_xlabel('samples (t)')\nax.set_ylabel('clock offset')\nax.set_title(\"$\\delta$=%i ns\" % (max(y) - min(y)))\n\nl = mlines.Line2D(x, y)\nax.add_line(l)\nax.set_xlim(min(x), max(x))\nax.set_ylim(min(y), max(y) )\n\nprint(min(y), max(y), max(y) - min(y))\n\n# Tweak spacing to prevent clipping of ylabel\nfig.tight_layout()\nplt.show()\n"
  },
  {
    "path": "scripts/utils.mk",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\n# Utils\n\nifeq ($(BUILDGUI), 1)\n  GUI\t\t= 'GUI '\n  GSPACE\t=\nelse\n  GUI\t\t=\n  GSPACE\t= \"    \"\nendif\n\n GOBJ\t\t= $(GSPACE)$(notdir $(strip $@))\n\n\nifeq ($(VERBOSE),1)\n  Q =\n  S =\nelse\n  Q = @\n  S = -s\nendif\n\n# Use empty print_* macros if either SILENT or VERBOSE.\nifeq ($(findstring 1,$(SILENT)$(VERBOSE)),1)\n  print_compile =\n  print_app_build =\n  print_fpic_compile =\n  print_shared_lib_compile =\n  print_plugin_obj_compile =\n  print_plugin_build =\n  print_install =\n  print_uninstall =\n  print_update =\n  print_asciidoc =\n  print_xsltproc =\n  print_install =\n  hide_xsltproc_output =\nelse\n  print_compile =\t\techo '  $(GUI)COMPILE            '$(GOBJ);\n  print_app_build =\t\techo '  $(GUI)BUILD              '$(GOBJ);\n  print_fpic_compile =\t\techo '  $(GUI)COMPILE FPIC       '$(GOBJ);\n  print_shared_lib_compile =\techo '  $(GUI)COMPILE SHARED LIB '$(GOBJ);\n  print_plugin_obj_compile =\techo '  $(GUI)COMPILE PLUGIN OBJ '$(GOBJ);\n  print_plugin_build =\t\techo '  $(GUI)BUILD PLUGIN       '$(GOBJ);\n  print_static_lib_build =\techo '  $(GUI)BUILD STATIC LIB   '$(GOBJ);\n  print_install =\t\techo '  $(GUI)INSTALL     '$(GSPACE)$1'\tto\t$(DESTDIR_SQ)$2';\n  print_update =\t\techo '  $(GUI)UPDATE             '$(GOBJ);\n  print_uninstall =\t\techo '  $(GUI)UNINSTALLING $(DESTDIR_SQ)$1';\n  print_asciidoc =\t\techo '  ASCIIDOC            '`basename $@`;\n  print_xsltproc =\t\techo '  XSLTPROC            '`basename $@`;\n  print_install =\t\techo '  INSTALL             '`basename $1`'\tto\t$(DESTDIR_SQ)'$2;\n  hide_xsltproc_output = 2> /dev/null\nendif\n\ndo_fpic_compile =\t\t\t\t\t\\\n\t($(print_fpic_compile)\t\t\t\t\\\n\t$(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) -fPIC $< -o $@)\n\ndo_compile =\t\t\t\t\t\t\t\\\n\t($(if $(GENERATE_PIC), $(do_fpic_compile),\t\t\\\n\t $(print_compile)\t\t\t\t\t\\\n\t $(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) $< -o $@))\n\ndo_app_build =\t\t\t\t\t\t\\\n\t($(print_app_build)\t\t\t\t\\\n\t$(CC) $^ -rdynamic -Wl,-rpath=$(libdir) -o $@ $(LDFLAGS) $(CONFIG_LIBS) $(LIBS))\n\ndo_build_static_lib =\t\t\t\t\\\n\t($(print_static_lib_build)\t\t\\\n\t$(RM) $@;  $(AR) rcs $@ $^)\n\ndo_compile_shared_library =\t\t\t\\\n\t($(print_shared_lib_compile)\t\t\\\n\t$(CC) --shared $^ '-Wl,-soname,$(1),-rpath=$$ORIGIN' -o $@ $(LDFLAGS) $(LIBS))\n\ndo_compile_plugin_obj =\t\t\t\t\\\n\t($(print_plugin_obj_compile)\t\t\\\n\t$(CC) -c $(CPPFLAGS) $(CFLAGS) -fPIC -o $@ $<)\n\ndo_plugin_build =\t\t\t\t\\\n\t($(print_plugin_build)\t\t\t\\\n\t$(CC) $(CFLAGS) $(LDFLAGS) -shared -nostartfiles -o $@ $<)\n\ndo_compile_python_plugin_obj =\t\t\t\\\n\t($(print_plugin_obj_compile)\t\t\\\n\t$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PYTHON_DIR_SQ) $(PYTHON_INCLUDES) -fPIC -o $@ $<)\n\ndo_python_plugin_build =\t\t\t\\\n\t($(print_plugin_build)\t\t\t\\\n\t$(CC) $< -shared $(LDFLAGS) $(PYTHON_LDFLAGS) -o $@)\n\ndefine make_version.h\n\t(echo '/* This file is automatically generated. Do not modify. */';\t\t\\\n\techo \\#define VERSION_CODE $(shell\t\t\t\t\t\t\\\n\texpr $(VERSION) \\* 256 + $(PATCHLEVEL));\t\t\t\t\t\\\n\techo '#define EXTRAVERSION ' $(EXTRAVERSION);\t\t\t\t\t\\\n\techo '#define VERSION_STRING \"'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'\"';\t\\\n\techo '#define FILE_VERSION '$(FILE_VERSION);\t\t\t\t\t\\\n\tif [ -d $(src)/.git ]; then\t\t\t\t\t\t\t\\\n\t  d=`git diff`;\t\t\t\t\t\t\t\t\t\\\n\t  x=\"\";\t\t\t\t\t\t\t\t\t\t\\\n\t  if [ ! -z \"$$d\" ]; then x=\"+\"; fi;\t\t\t\t\t\t\\\n\t  echo '#define VERSION_GIT \"'$(shell \t\t\t\t\t\t\\\n\t\tgit log -1 --pretty=format:\"%H\" 2>/dev/null)$$x'\"';\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\t\t\\\n\t  echo '#define VERSION_GIT \"not-a-git-repo\"';\t\t\t\t\t\\\n\tfi\t\t\t\t\t\t\t\t\t\t\\\n\t) > $1\nendef\n\ndefine update_version.h\n\t($(call make_version.h, $@.tmp);\t\t\t\t\\\n\tif [ -r $@ ] && cmp -s $@ $@.tmp; then\t\t\t\t\\\n\t\trm -f $@.tmp;\t\t\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\\\n\t\t$(print_update)\t\t\t\t\t\t\\\n\t\tmv -f $@.tmp $@;\t\t\t\t\t\\\n\tfi);\nendef\n\ndefine update_dir\n\t(echo $1 > $@.tmp;\t\\\n\tif [ -r $@ ] && cmp -s $@ $@.tmp; then\t\t\t\t\\\n\t\trm -f $@.tmp;\t\t\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\\\n\t\t$(print_update)\t\t\t\t\t\t\\\n\t\tmv -f $@.tmp $@;\t\t\t\t\t\\\n\tfi);\nendef\n\ndefine build_prefix\n\t(echo $1 > $@.tmp;\t\\\n\tif [ -r $@ ] && cmp -s $@ $@.tmp; then\t\t\t\t\\\n\t\trm -f $@.tmp;\t\t\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\\\n\t\t$(print_update)\t\t\t\t\t\t\\\n\t\tmv -f $@.tmp $@;\t\t\t\t\t\\\n\tfi);\nendef\n\ndefine do_install\n\t$(print_install)\t\t\t\t\\\n\tif [ ! -d '$(DESTDIR_SQ)$2' ]; then\t\t\\\n\t\t$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2';\t\\\n\tfi;\t\t\t\t\t\t\\\n\t$(INSTALL) $(if $3,-m $3,) $1 '$(DESTDIR_SQ)$2'\nendef\n\ndefine do_install_data\n\t$(print_install)\t\t\t\t\\\n\tif [ ! -d '$(DESTDIR_SQ)$2' ]; then\t\t\\\n\t\t$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2';\t\\\n\tfi;\t\t\t\t\t\t\\\n\t$(INSTALL) -m 644 $1 '$(DESTDIR_SQ)$2'\nendef\n\ndefine do_install_pkgconfig_file\n\tif [ -n \"${pkgconfig_dir}\" ]; then \t\t\t\t\t\\\n\t\t$(call do_install,$(PKG_CONFIG_FILE),$(pkgconfig_dir),644); \t\\\n\telse \t\t\t\t\t\t\t\t\t\\\n\t\t(echo Failed to locate pkg-config directory) 1>&2;\t\t\\\n\tfi\nendef\n\ndefine do_make_pkgconfig_file\n\t$(print_app_build)\n\t$(Q)cp -f $(srctree)/${PKG_CONFIG_SOURCE_FILE}.template ${PKG_CONFIG_FILE};\t\\\n\tsed -i \"s|INSTALL_PREFIX|${1}|g\" ${PKG_CONFIG_FILE}; \t\t\\\n\tsed -i \"s|LIB_VERSION|${LIBTRACECMD_VERSION}|g\" ${PKG_CONFIG_FILE}; \\\n\tsed -i \"s|LIB_DIR|$(libdir)|g\" ${PKG_CONFIG_FILE}; \\\n\tsed -i \"s|LIBTRACEFS_MIN_VERSION|$(LIBTRACEFS_MIN_VERSION)|g\" ${PKG_CONFIG_FILE}; \\\n\tsed -i \"s|HEADER_DIR|$(includedir)/trace-cmd|g\" ${PKG_CONFIG_FILE};\nendef\n\ndo_asciidoc_build =\t\t\t\t\\\n\t($(print_asciidoc)\t\t\t\\\n\t asciidoc -d manpage -b docbook -o $@ $<)\n\ndo_xsltproc_build =\t\t\t\t\\\n\t($(print_xsltproc)\t\t\t\\\n\t xsltproc --nonet -o $@ ${MANPAGE_DOCBOOK_XSL} $< $(hide_xsltproc_output))\n\n#\n# asciidoc requires a synopsis, but file format man pages (5) do\n# not require them. This removes it from the file in the final step.\ndefine remove_synopsis\n\t(sed -e '/^\\.SH \"SYNOPSIS\"/,/ignore/d' $1 > $1.tmp;\\\n\t mv $1.tmp $1)\nendef\n\ndefine do_install_docs\n\t$(print_install)\t\t\t\t\\\n\tif [ ! -d '$(DESTDIR_SQ)$2' ]; then\t\t\\\n\t\t$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2';\t\\\n\tfi;\t\t\t\t\t\t\\\n\t$(INSTALL) -m 644 $1 '$(DESTDIR_SQ)$2'\nendef\n\nifneq ($(findstring $(MAKEFLAGS),s),s)\nifneq ($(V),1)\n\tQUIET_ASCIIDOC\t= @echo '  ASCIIDOC  '$@;\n\tQUIET_XMLTO\t= @echo '  XMLTO    '$@;\n\tQUIET_SUBDIR0\t= +@subdir=\n\tQUIET_SUBDIR1\t= ;$(NO_SUBDIR) \\\n\t\t\t   echo '  SUBDIR   ' $$subdir; \\\n\t\t\t  $(MAKE) $(PRINT_DIR) -C $$subdir\n\texport V\nendif\nendif\n"
  },
  {
    "path": "tracecmd/.gitignore",
    "content": "trace-cmd\n"
  },
  {
    "path": "tracecmd/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\nVERSION := $(TC_VERSION)\nPATCHLEVEL := $(TC_PATCHLEVEL)\nEXTRAVERSION := $(TC_EXTRAVERSION)\n\nbdir:=$(obj)/tracecmd\n\nTC_VERSION := $(bdir)/include/tc_version.h\nTARGETS = $(bdir)/trace-cmd $(TC_VERSION)\n\nBUILDGUI := 0\ninclude $(src)/scripts/utils.mk\n\nCFLAGS += -I$(bdir)/include\n\nTRACE_CMD_OBJS =\nTRACE_CMD_OBJS += trace-cmd.o\nTRACE_CMD_OBJS += trace-record.o\nTRACE_CMD_OBJS += trace-read.o\nTRACE_CMD_OBJS += trace-split.o\nTRACE_CMD_OBJS += trace-listen.o\nTRACE_CMD_OBJS += trace-stack.o\nTRACE_CMD_OBJS += trace-hist.o\nTRACE_CMD_OBJS += trace-mem.o\nTRACE_CMD_OBJS += trace-snapshot.o\nTRACE_CMD_OBJS += trace-stat.o\nTRACE_CMD_OBJS += trace-profile.o\nTRACE_CMD_OBJS += trace-stream.o\nTRACE_CMD_OBJS += trace-record.o\nTRACE_CMD_OBJS += trace-restore.o\nTRACE_CMD_OBJS += trace-check-events.o\nTRACE_CMD_OBJS += trace-show.o\nTRACE_CMD_OBJS += trace-list.o\nTRACE_CMD_OBJS += trace-usage.o\nTRACE_CMD_OBJS += trace-dump.o\nTRACE_CMD_OBJS += trace-clear.o\nTRACE_CMD_OBJS += trace-vm.o\nTRACE_CMD_OBJS += trace-convert.o\nTRACE_CMD_OBJS += trace-attach.o\nTRACE_CMD_OBJS += trace-agent.o\nTRACE_CMD_OBJS += trace-tsync.o\nTRACE_CMD_OBJS += trace-setup-guest.o\nTRACE_CMD_OBJS += trace-sqlhist.o\nifeq ($(VSOCK_DEFINED), 1)\nTRACE_CMD_OBJS += trace-vsock.o\nendif\n\nALL_OBJS := $(TRACE_CMD_OBJS:%.o=$(bdir)/%.o)\n\nall_objs := $(sort $(ALL_OBJS))\nall_deps := $(all_objs:$(bdir)/%.o=$(bdir)/.%.d)\n\nLPTHREAD ?= -lpthread\nLRT ?= -lrt\n\nCONFIG_INCLUDES =\nCONFIG_LIBS\t= $(LRT) $(LPTHREAD) $(TRACE_LIBS) $(ZLIB_LDLAGS) $(LIBZSTD_LDLAGS)\nCONFIG_FLAGS\t=\n\nall: $(TARGETS)\n\n$(bdir):\n\t@mkdir -p $(bdir)\n\n$(bdir)/include: | $(bdir)\n\t@mkdir -p $(bdir)/include\n\n$(TC_VERSION): force | $(bdir)/include\n\t$(Q)$(call update_version.h)\n\n$(all_deps): | $(bdir)\n$(all_objs): | $(bdir)\n\n$(bdir)/trace-cmd: $(ALL_OBJS)\n\t$(Q)$(do_app_build)\n\n$(bdir)/trace-cmd: $(LIBTRACECMD_STATIC)\n\n$(bdir)/%.o: %.c\n\t$(Q)$(call do_compile)\n\n$(all_deps): $(bdir)/.%.d: %.c\n\t$(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@\n\n$(all_deps): $(TC_VERSION)\n\n$(all_objs): $(bdir)/%.o : $(bdir)/.%.d\n\ndep_includes := $(wildcard $(DEPS))\n\nifneq ($(dep_includes),)\n  include $(dep_includes)\nendif\n\nclean:\n\t$(RM) $(bdir)/*.a $(bdir)/*.so $(bdir)/*.o $(bdir)/.*.d $(TARGETS)\n\nforce:\n.PHONY: clean\n"
  },
  {
    "path": "tracecmd/include/bug.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n#ifndef __TRACE_CMD_BUG\n#define __TRACE_CMD_BUG\n\n#define unlikely(cond)\t__builtin_expect(!!(cond), 0)\n\n#define WARN_ONCE(cond, fmt, ...)\t\t\t\\\n\t({\t\t\t\t\t\t\\\n\t\tint __c__ = cond;\t\t\t\\\n\t\tif (unlikely(__c__)) {\t\t\t\\\n\t\t\twarning(fmt, ##__VA_ARGS__);\t\\\n\t\t}\t\t\t\t\t\\\n\t\t__c__;\t\t\t\t\t\\\n\t})\n#endif /* __TRACE_CMD_BUG */\n"
  },
  {
    "path": "tracecmd/include/list.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#ifndef __LIST_H\n#define __LIST_H\n\n#define offset_of(type, field)\t\t__builtin_offsetof(type, field)\n#define container_of(p, type, field)\t(type *)((long)p - offset_of(type, field))\n\nstruct list_head {\n\tstruct list_head *next;\n\tstruct list_head *prev;\n};\n\nstatic inline void list_head_init(struct list_head *list)\n{\n\tlist->next = list;\n\tlist->prev = list;\n}\n\nstatic inline void list_add(struct list_head *p, struct list_head *head)\n{\n\tstruct list_head *next = head->next;\n\n\tp->prev = head;\n\tp->next = next;\n\tnext->prev = p;\n\thead->next = p;\n}\n\nstatic inline void list_add_tail(struct list_head *p, struct list_head *head)\n{\n\tstruct list_head *prev = head->prev;\n\n\tp->prev = prev;\n\tp->next = head;\n\tprev->next = p;\n\thead->prev = p;\n}\n\nstatic inline void list_del(struct list_head *p)\n{\n\tstruct list_head *next = p->next;\n\tstruct list_head *prev = p->prev;\n\n\tnext->prev = prev;\n\tprev->next = next;\n}\n\nstatic inline int list_empty(struct list_head *list)\n{\n\treturn list->next == list;\n}\n\n#define list_for_each_entry(p, list, field)\t\t\\\n\tfor (p = container_of((list)->next, typeof(*p), field);\t\\\n\t     &(p)->field != list;\t\t\t\t\\\n\t     p = container_of((p)->field.next, typeof(*p), field))\n\n#define list_for_each_entry_safe(p, n, list, field)\t\t\t\\\n\tfor (p = container_of((list)->next, typeof(*p), field),\t\t\\\n\t\t     n = container_of((p)->field.next, typeof(*p), field); \\\n\t     &(p)->field != list;\t\t\t\t\t\\\n\t     p = n, n = container_of((p)->field.next, typeof(*p), field))\n\n#endif /* __LIST_H */\n"
  },
  {
    "path": "tracecmd/include/trace-local.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 */\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#ifndef __TRACE_LOCAL_H\n#define __TRACE_LOCAL_H\n\n#include <sys/types.h>\n#include <dirent.h>\t/* for DIR */\n#include <ctype.h>\t/* for isdigit() */\n#include <errno.h>\n#include <limits.h>\n#include <netinet/tcp.h>\n#include <netinet/in.h>\n\n#include \"trace-cmd-private.h\"\n#include \"event-utils.h\"\n\n#define TRACE_AGENT_DEFAULT_PORT\t823\n\n#define DEFAULT_INPUT_FILE\t\"trace.dat\"\n#define GUEST_PIPE_NAME\t\t\"trace-pipe-cpu\"\n#define GUEST_DIR_FMT\t\t\"/var/lib/trace-cmd/virt/%s\"\n#define GUEST_FIFO_FMT\t\tGUEST_DIR_FMT \"/\" GUEST_PIPE_NAME \"%d\"\n#define VIRTIO_FIFO_FMT\t\t\"/dev/virtio-ports/\" GUEST_PIPE_NAME \"%d\"\n\n/* fix stupid glib guint64 typecasts and printf formats */\ntypedef unsigned long long u64;\n\nstruct buffer_instance;\n\n#define __printf(a, b) __attribute__((format(printf,a,b)))\n\n__printf(1,2)\nvoid warning(const char *fmt, ...);\n\n/* for local shared information with trace-cmd executable */\n\nvoid usage(char **argv);\n\nextern int silence_warnings;\nextern int show_status;\n\nvoid trace_set_loglevel(int level);\nint trace_set_verbose(char *level);\n\nenum port_type {\n\tUSE_UDP\t\t\t= 0,\t/* Default setting */\n\tUSE_TCP,\n\tUSE_VSOCK\n};\n\nstruct pid_record_data {\n\tint\t\t\tpid;\n\tint\t\t\tbrass[2];\n\tint\t\t\tcpu;\n\tint\t\t\tclosed;\n\tstruct tracecmd_input\t*stream;\n\tstruct buffer_instance\t*instance;\n\tstruct tep_record\t*record;\n};\n\nvoid show_file(const char *name);\n\nstruct tracecmd_input *read_trace_header(const char *file, int flags);\nint read_trace_files(void);\n\nvoid trace_record(int argc, char **argv);\n\nvoid trace_stop(int argc, char **argv);\n\nvoid trace_restart(int argc, char **argv);\n\nvoid trace_reset(int argc, char **argv);\n\nvoid trace_start(int argc, char **argv);\n\nvoid trace_set(int argc, char **argv);\n\nvoid trace_extract(int argc, char **argv);\n\nvoid trace_stream(int argc, char **argv);\n\nvoid trace_profile(int argc, char **argv);\n\nvoid trace_report(int argc, char **argv);\n\nvoid trace_split(int argc, char **argv);\n\nvoid trace_listen(int argc, char **argv);\n\nvoid trace_agent(int argc, char **argv);\n\nvoid trace_setup_guest(int argc, char **argv);\n\nvoid trace_restore(int argc, char **argv);\n\nvoid trace_clear(int argc, char **argv);\n\nvoid trace_check_events(int argc, char **argv);\n\nvoid trace_stack(int argc, char **argv);\n\nvoid trace_option(int argc, char **argv);\n\nvoid trace_hist(int argc, char **argv);\n\nvoid trace_snapshot(int argc, char **argv);\n\nvoid trace_mem(int argc, char **argv);\n\nvoid trace_stat(int argc, char **argv);\n\nvoid trace_show(int argc, char **argv);\n\nvoid trace_list(int argc, char **argv);\n\nvoid trace_usage(int argc, char **argv);\n\nvoid trace_dump(int argc, char **argv);\n\nvoid trace_attach(int argc, char **argv);\n\nvoid trace_convert(int argc, char **argv);\n\nvoid trace_sqlhist (int argc, char **argv);\n\nint trace_record_agent(struct tracecmd_msg_handle *msg_handle,\n\t\t       int cpus, int *fds,\n\t\t       int argc, char **argv,\n\t\t       bool use_fifos, struct tracecmd_time_sync *tsync,\n\t\t       unsigned long long trace_id, int rcid, const char *host);\n\nstruct hook_list;\n\nvoid trace_init_profile(struct tracecmd_input *handle, struct hook_list *hooks,\n\t\t\tint global);\nint do_trace_profile(void);\nvoid trace_profile_set_merge_like_comms(void);\n\nstruct tracecmd_input *\ntrace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus,\n\t\t  struct hook_list *hooks,\n\t\t  tracecmd_handle_init_func handle_init, int global);\nint trace_stream_read(struct pid_record_data *pids, int nr_pids, long sleep_us);\n\nvoid trace_show_data(struct tracecmd_input *handle, struct tep_record *record);\n\n/* --- event interation --- */\n\n/*\n * Use this to iterate through the event directories\n */\n\n\nenum event_process {\n\tPROCESSED_NONE,\n\tPROCESSED_EVENT,\n\tPROCESSED_SYSTEM\n};\n\nenum process_type {\n\tPROCESS_EVENT,\n\tPROCESS_SYSTEM\n};\n\nstruct event_iter {\n\tDIR *system_dir;\n\tDIR *event_dir;\n\tstruct dirent *system_dent;\n\tstruct dirent *event_dent;\n};\n\nenum event_iter_type {\n\tEVENT_ITER_NONE,\n\tEVENT_ITER_SYSTEM,\n\tEVENT_ITER_EVENT\n};\n\nstruct event_iter *trace_event_iter_alloc(const char *path);\nenum event_iter_type trace_event_iter_next(struct event_iter *iter,\n\t\t\t\t\t   const char *path, const char *system);\nvoid trace_event_iter_free(struct event_iter *iter);\n\nchar *append_file(const char *dir, const char *name);\nchar *get_file_content(const char *file);\n\nchar *strstrip(char *str);\n\n/* --- instance manipulation --- */\n\nenum buffer_instance_flags {\n\tBUFFER_FL_KEEP\t\t= 1 << 0,\n\tBUFFER_FL_PROFILE\t= 1 << 1,\n\tBUFFER_FL_GUEST\t\t= 1 << 2,\n\tBUFFER_FL_AGENT\t\t= 1 << 3,\n\tBUFFER_FL_HAS_CLOCK\t= 1 << 4,\n\tBUFFER_FL_TSC2NSEC\t= 1 << 5,\n\tBUFFER_FL_NETWORK\t= 1 << 6,\n\tBUFFER_FL_PROXY\t\t= 1 << 7,\n};\n\nstruct func_list {\n\tstruct func_list *next;\n\tconst char *func;\n\tconst char *mod;\n};\n\nstruct pid_addr_maps {\n\tstruct pid_addr_maps\t\t*next;\n\tstruct tracecmd_proc_addr_map\t*lib_maps;\n\tunsigned int\t\t\tnr_lib_maps;\n\tchar\t\t\t\t*proc_name;\n\tint\t\t\t\tpid;\n};\n\nstruct opt_list {\n\tstruct opt_list *next;\n\tconst char\t*option;\n};\n\nstruct filter_pids {\n\tstruct filter_pids *next;\n\tint pid;\n\tint exclude;\n};\n\nstruct tsc_nsec {\n\tint mult;\n\tint shift;\n\tunsigned long long offset;\n};\n\nstruct buffer_instance {\n\tstruct buffer_instance\t*next;\n\tchar\t\t\t*name;\n\tstruct tracefs_instance\t*tracefs;\n\tunsigned long long\ttrace_id;\n\tchar\t\t\t*cpumask;\n\tchar\t\t\t*output_file;\n\tconst char\t\t*temp_dir;\n\tchar\t\t\t*temp_file;\n\tstruct event_list\t*events;\n\tstruct event_list\t**event_next;\n\tbool\t\t\tdelete;\n\n\tstruct event_list\t*sched_switch_event;\n\tstruct event_list\t*sched_wakeup_event;\n\tstruct event_list\t*sched_wakeup_new_event;\n\n\tconst char\t\t*plugin;\n\tchar\t\t\t*filter_mod;\n\tstruct func_list\t*filter_funcs;\n\tstruct func_list\t*notrace_funcs;\n\n\tstruct opt_list\t\t*options;\n\tstruct filter_pids\t*filter_pids;\n\tstruct filter_pids\t*process_pids;\n\tchar\t\t\t*common_pid_filter;\n\tint\t\t\tnr_filter_pids;\n\tint\t\t\tlen_filter_pids;\n\tint\t\t\tnr_process_pids;\n\tbool\t\t\tptrace_child;\n\n\tint\t\t\thave_set_event_pid;\n\tint\t\t\thave_event_fork;\n\tint\t\t\thave_func_fork;\n\tint\t\t\tget_procmap;\n\n\tconst char\t\t*clock;\n\tunsigned int\t\t*client_ports;\n\n\tstruct trace_seq\t*s_save;\n\tstruct trace_seq\t*s_print;\n\n\tstruct tracecmd_input\t*handle;\n\n\tstruct tracecmd_msg_handle *msg_handle;\n\tstruct tracecmd_output *network_handle;\n\tconst char\t\t*host;\n\n\tstruct pid_addr_maps\t*pid_maps;\n\n\tchar\t\t\t*max_graph_depth;\n\n\tint\t\t\tflags;\n\tint\t\t\ttracing_on_init_val;\n\tint\t\t\ttracing_on_fd;\n\tint\t\t\tbuffer_size;\n\tint\t\t\told_buffer_size;\n\tint\t\t\tsubbuf_size;\n\tint\t\t\told_subbuf_size;\n\tint\t\t\tcpu_count;\n\n\tint\t\t\tproxy_fd;\n\n\tint\t\t\targc;\n\tchar\t\t\t**argv;\n\n\tchar\t\t\t*last_boot_info;\n\n\tstruct addrinfo\t\t*result;\n\tunsigned int\t\tcid;\n\tunsigned int\t\tport;\n\tint\t\t\t*fds;\n\tbool\t\t\tuse_fifos;\n\n\tenum port_type\t\tport_type;\t/* Default to USE_UDP (zero) */\n\tint\t\t\ttsync_loop_interval;\n\tstruct tracecmd_time_sync *tsync;\n};\n\nvoid init_top_instance(void);\n\nextern struct buffer_instance top_instance;\nextern struct buffer_instance *buffer_instances;\nextern struct buffer_instance *first_instance;\n\n#define for_each_instance(i) for (i = buffer_instances; i; i = (i)->next)\n#define for_all_instances(i) for (i = first_instance; i; \\\n\t\t\t\t  i = i == &top_instance ? buffer_instances : (i)->next)\n\n#define is_agent(instance)\t((instance)->flags & BUFFER_FL_AGENT)\n#define is_guest(instance)\t((instance)->flags & BUFFER_FL_GUEST)\n#define is_proxy(instance)\t((instance)->flags & BUFFER_FL_PROXY)\n#define is_network(instance)\t((instance)->flags & BUFFER_FL_NETWORK)\n#define is_proxy_server(instance)\t\t\t\t\t\\\n\t((instance)->msg_handle &&\t\t\t\t\t\\\n\t (instance)->msg_handle->flags & TRACECMD_MSG_FL_PROXY)\n\n#define START_PORT_SEARCH 1500\n#define MAX_PORT_SEARCH 6000\n\nstruct sockaddr_storage;\n\nint trace_net_make(int port, enum port_type type);\nint trace_net_search(int start_port, int *sfd, enum port_type type);\nint trace_net_print_connection(int fd);\nbool trace_net_cmp_connection(struct sockaddr_storage *addr, const char *name);\nbool trace_net_cmp_connection_fd(int fd, const char *name);\n\nstruct buffer_instance *allocate_instance(const char *name);\nvoid add_instance(struct buffer_instance *instance, int cpu_count);\nvoid update_first_instance(struct buffer_instance *instance, int topt);\n\nvoid show_instance_file(struct buffer_instance *instance, const char *name);\nvoid show_options(const char *prefix, struct buffer_instance *buffer, const char *re);\n\nstruct trace_guest {\n\tstruct tracefs_instance *instance;\n\tchar *name;\n\tunsigned long long trace_id;\n\tint cid;\n\tint pid;\n\tint cpu_max;\n\tint *cpu_pid;\n\tint *task_pids;\n};\nstruct trace_guest *trace_get_guest(unsigned int cid, const char *name);\nbool trace_have_guests_pid(void);\nvoid read_qemu_guests(void);\nint get_guest_pid(unsigned int guest_cid);\nint get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu);\nvoid trace_add_guest_info(struct tracecmd_output *handle, struct buffer_instance *instance);\n\nstruct tracecmd_time_sync *\ntrace_tsync_as_host(int fd, unsigned long long trace_id,\n\t\t    int loop_interval, int guest_id,\n\t\t    int guest_cpus, const char *proto_name,\n\t\t    const char *clock);\n\nstruct tracecmd_time_sync *\ntrace_tsync_as_guest(int fd, const char *tsync_proto, const char *clock,\n\t       unsigned int remote_id, unsigned int local_id);\n\nchar *strparse(char *str, char delim, char **save);\n\n/* moved from trace-cmd.h */\nvoid tracecmd_remove_instances(void);\nint tracecmd_add_event(const char *event_str, int stack);\nvoid tracecmd_enable_events(void);\nvoid tracecmd_disable_all_tracing(int disable_tracer);\nvoid tracecmd_disable_tracing(void);\nvoid tracecmd_enable_tracing(void);\nvoid tracecmd_stat_cpu(struct trace_seq *s, int cpu);\n\nint tracecmd_host_tsync(struct buffer_instance *instance,\n\t\t\t unsigned int tsync_port);\nvoid tracecmd_host_tsync_complete(struct buffer_instance *instance);\nconst char *tracecmd_guest_tsync(struct tracecmd_tsync_protos *tsync_protos,\n\t\t\t\t char *clock, unsigned int *tsync_port,\n\t\t\t\t pthread_t *thr_id);\n\nint trace_make_vsock(unsigned int port);\nint trace_get_vsock_port(int sd, unsigned int *port);\nint trace_open_vsock(unsigned int cid, unsigned int port);\n\nint get_local_cid(unsigned int *cid);\n\nchar *trace_get_guest_file(const char *file, const char *guest);\n\n#ifdef VSOCK\nint tcmd_vsock_open(unsigned int cid, unsigned int port);\nint tcmd_vsock_make(unsigned int port);\nint tcmd_vsock_make_any(void);\nint get_vsocket_params(int fd, unsigned int *lcid, unsigned int *rcid);\nint tcmd_vsock_get_port(int sd, unsigned int *port);\nbool tcmd_vsock_can_splice_read(void);\nint tcmd_vsock_local_cid(void);\nint trace_vsock_print_connection(int fd);\n#else\nstatic inline int tcmd_vsock_open(unsigned int cid, unsigned int port)\n{\n\treturn -ENOTSUP;\n}\n\nstatic inline int tcmd_vsock_make(unsigned int port)\n{\n\treturn -ENOTSUP;\n\n}\n\nstatic inline int tcmd_vsock_make_any(void)\n{\n\treturn -ENOTSUP;\n\n}\n\nstatic inline int get_vsocket_params(int fd, unsigned int *lcid, unsigned int *rcid)\n{\n\treturn -ENOTSUP;\n}\n\nstatic inline int tcmd_vsock_get_port(int sd, unsigned int *port)\n{\n\treturn -ENOTSUP;\n}\n\nstatic inline bool tcmd_vsock_can_splice_read(void)\n{\n\treturn false;\n}\n\nstatic inline int tcmd_vsock_local_cid(void)\n{\n\treturn -ENOTSUP;\n}\nstatic inline int trace_vsock_print_connection(int fd)\n{\n\treturn -1;\n}\n#endif /* VSOCK */\n\n/* No longer in event-utils.h */\n__printf(1,2)\nvoid __noreturn die(const char *fmt, ...); /* Can be overriden */\nvoid *malloc_or_die(unsigned int size); /* Can be overridden */\n__printf(1,2)\nvoid __noreturn __die(const char *fmt, ...);\nvoid __noreturn _vdie(const char *fmt, va_list ap);\n\nstatic inline bool is_digits(const char *s)\n{\n\tfor (; *s; s++)\n\t\tif (!isdigit(*s))\n\t\t\treturn false;\n\treturn true;\n}\n\nbool trace_tsc2nsec_is_supported(void);\n\nvoid make_pid_name(char *buf, const char *pidfile_basename);\nvoid remove_pid_file(const char *pidfile_basename);\nvoid make_pid_file(const char *pidfile_basename);\n\nstatic inline void set_tcp_no_delay(int sockfd, int socktype)\n{\n\tint flag = 1;\n\n\tif (socktype == SOCK_STREAM)\n\t\tsetsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));\n}\n\n#endif /* __TRACE_LOCAL_H */\n"
  },
  {
    "path": "tracecmd/meson.build",
    "content": "# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nsources = [\n    'trace-agent.c',\n    'trace-attach.c',\n    'trace-check-events.c',\n    'trace-clear.c',\n    'trace-cmd.c',\n    'trace-convert.c',\n    'trace-dump.c',\n    'trace-hist.c',\n    'trace-list.c',\n    'trace-listen.c',\n    'trace-mem.c',\n    'trace-profile.c',\n    'trace-read.c',\n    'trace-record.c',\n    'trace-restore.c',\n    'trace-setup-guest.c',\n    'trace-show.c',\n    'trace-snapshot.c',\n    'trace-split.c',\n    'trace-stack.c',\n    'trace-stat.c',\n    'trace-stream.c',\n    'trace-tsync.c',\n    'trace-usage.c',\n    'trace-vm.c',\n    'trace-sqlhist.c',\n]\n\nif vsock_defined\n    sources += 'trace-vsock.c'\nendif\n\ntrace_cmd_incdir = include_directories(['.', 'include'])\n\nexecutable(\n    'trace-cmd',\n    sources,\n    dependencies: [\n        libtraceevent_dep,\n        libtracefs_dep,\n        zlib_dep,\n        libzstd_dep,\n        audit_dep],\n    include_directories: [\n        incdir,\n        trace_cmd_incdir,\n        libtracecmd_incdir,\n        libtracecmd_private_incdir,\n        libtracecmd_ext_incdir],\n    link_with: [static_libtracecmd],\n    install: true,\n    install_dir: bindir)\n\ninstall_data(\n    'trace-cmd.bash',\n    install_dir: datadir + '/bash-completion/completions')\n"
  },
  {
    "path": "tracecmd/trace-agent.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2018 VMware Inc, Slavomir Kaslev <kaslevs@vmware.com>\n *\n * based on prior implementation by Yoshihiro Yunomae\n * Copyright (C) 2013 Hitachi, Ltd.\n * Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>\n */\n\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/socket.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <pthread.h>\n\n#include \"trace-local.h\"\n#include \"trace-msg.h\"\n\n#define GUEST_NAME\t\"::GUEST::\"\n\n#define dprint(fmt, ...)\ttracecmd_debug(fmt, ##__VA_ARGS__)\n\nstatic void make_vsocks(int nr, int *fds, unsigned int *ports)\n{\n\tunsigned int port;\n\tint i, fd, ret;\n\n\tfor (i = 0; i < nr; i++) {\n\t\tfd = tcmd_vsock_make_any();\n\t\tif (fd < 0)\n\t\t\tdie(\"Failed to open vsocket\");\n\n\t\tret = tcmd_vsock_get_port(fd, &port);\n\t\tif (ret < 0)\n\t\t\tdie(\"Failed to get vsocket address\");\n\n\t\tfds[i] = fd;\n\t\tports[i] = port;\n\t}\n}\n\nstatic void make_net(int nr, int *fds, unsigned int *ports)\n{\n\tint port;\n\tint i, fd;\n\tint start_port = START_PORT_SEARCH;\n\n\tfor (i = 0; i < nr; i++) {\n\t\tport = trace_net_search(start_port, &fd, USE_TCP);\n\t\tif (port < 0)\n\t\t\tdie(\"Failed to open socket\");\n\t\tif (listen(fd, 5) < 0)\n\t\t\tdie(\"Failed to listen on port %d\\n\", port);\n\t\tfds[i] = fd;\n\t\tports[i] = port;\n\t\tdprint(\"CPU[%d]: fd:%d port:%d\\n\", i, fd, port);\n\t\tstart_port = port + 1;\n\t}\n}\n\nstatic void make_sockets(int nr, int *fds, unsigned int *ports,\n\t\t\t const char * network)\n{\n\tif (network)\n\t\treturn make_net(nr, fds, ports);\n\telse\n\t\treturn make_vsocks(nr, fds, ports);\n}\n\nstatic int open_agent_fifos(int nr_cpus, int *fds)\n{\n\tchar path[PATH_MAX];\n\tint i, fd, ret;\n\n\tfor (i = 0; i < nr_cpus; i++) {\n\t\tsnprintf(path, sizeof(path), VIRTIO_FIFO_FMT, i);\n\t\tfd = open(path, O_WRONLY);\n\t\tif (fd < 0) {\n\t\t\tret = -errno;\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\tfds[i] = fd;\n\t}\n\n\treturn 0;\n\ncleanup:\n\twhile (--i >= 0)\n\t\tclose(fds[i]);\n\n\treturn ret;\n}\n\nstatic char *get_clock(int argc, char **argv)\n{\n\tint i;\n\n\tif (!argc || !argv)\n\t\treturn NULL;\n\n\tfor (i = 0; i < argc - 1; i++) {\n\t\tif (!strcmp(\"-C\", argv[i]))\n\t\t\treturn argv[i+1];\n\t}\n\treturn NULL;\n}\n\nstatic void trace_print_connection(int fd, const char *network)\n{\n\tint ret;\n\n\tif (network)\n\t\tret = trace_net_print_connection(fd);\n\telse\n\t\tret = trace_vsock_print_connection(fd);\n\tif (ret < 0)\n\t\ttracecmd_debug(\"Could not print connection fd:%d\\n\", fd);\n}\n\nstatic int wait_for_connection(int fd)\n{\n\tint sd;\n\n\tif (fd < 0)\n\t\treturn -1;\n\n\twhile (true) {\n\t\ttracecmd_debug(\"Listening on fd:%d\\n\", fd);\n\t\tsd = accept(fd, NULL, NULL);\n\t\ttracecmd_debug(\"Accepted fd:%d\\n\", sd);\n\t\tif (sd < 0) {\n\t\t\tif (errno == EINTR)\n\t\t\t\tcontinue;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\t}\n\tclose(fd);\n\treturn sd;\n}\n\nstatic void agent_handle(int sd, int nr_cpus, int page_size,\n\t\t\t int cid, int rcid, const char *network)\n{\n\tstruct tracecmd_tsync_protos *tsync_protos = NULL;\n\tstruct tracecmd_time_sync *tsync = NULL;\n\tstruct tracecmd_msg_handle *msg_handle;\n\tconst char *tsync_proto = NULL;\n\tstruct trace_guest *guest;\n\tunsigned long long peer_trace_id;\n\tunsigned long long trace_id;\n\tunsigned long flags = rcid >= 0 ? TRACECMD_MSG_FL_PROXY : 0;\n\tenum tracecmd_time_sync_role tsync_role = TRACECMD_TIME_SYNC_ROLE_GUEST;\n\tunsigned int remote_id;\n\tunsigned int local_id;\n\tunsigned int tsync_port = 0;\n\tunsigned int *ports;\n\tunsigned int client_cpus = 0;\n\tunsigned int guests = 0;\n\tchar **argv = NULL;\n\tint argc = 0;\n\tbool use_fifos;\n\tint *fds;\n\tint ret;\n\tint fd;\n\n\tfds = calloc(nr_cpus, sizeof(*fds));\n\tports = calloc(nr_cpus, sizeof(*ports));\n\tif (!fds || !ports)\n\t\tdie(\"Failed to allocate memory\");\n\n\tmsg_handle = tracecmd_msg_handle_alloc(sd, flags);\n\tif (!msg_handle)\n\t\tdie(\"Failed to allocate message handle\");\n\n\tif (rcid >= 0) {\n\t\ttsync_role = TRACECMD_TIME_SYNC_ROLE_HOST;\n\t\tret = tracecmd_msg_recv_trace_proxy(msg_handle, &argc, &argv,\n\t\t\t\t\t\t    &use_fifos, &peer_trace_id,\n\t\t\t\t\t\t    &tsync_protos,\n\t\t\t\t\t\t    &client_cpus,\n\t\t\t\t\t\t    &guests);\n\t\t/* Update the guests peer_trace_id */\n\t\tguest = trace_get_guest(rcid, NULL);\n\t\tif (guest)\n\t\t\tguest->trace_id = peer_trace_id;\n\t} else {\n\t\tret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,\n\t\t\t\t\t\t  &use_fifos, &peer_trace_id,\n\t\t\t\t\t\t  &tsync_protos);\n\t}\n\tif (ret < 0)\n\t\tdie(\"Failed to receive trace request\");\n\n\ttsync_proto = tracecmd_tsync_get_proto(tsync_protos, get_clock(argc, argv),\n\t\t\t\t\t       tsync_role);\n\n\tif (use_fifos && open_agent_fifos(nr_cpus, fds))\n\t\tuse_fifos = false;\n\n\tif (!use_fifos)\n\t\tmake_sockets(nr_cpus, fds, ports, network);\n\tif (tsync_proto) {\n\t\tif (network) {\n\t\t\t/* For now just use something */\n\t\t\tremote_id = 2;\n\t\t\tlocal_id = 1;\n\t\t\ttsync_port = trace_net_search(START_PORT_SEARCH, &fd, USE_TCP);\n\t\t\tif (listen(fd, 5) < 0)\n\t\t\t\tdie(\"Failed to listen on %d\\n\", tsync_port);\n\t\t} else {\n\t\t\tif (get_vsocket_params(msg_handle->fd, &local_id,\n\t\t\t\t\t       &remote_id)) {\n\t\t\t\twarning(\"Failed to get local and remote ids\");\n\t\t\t\t/* Just make something up */\n\t\t\t\tremote_id = -1;\n\t\t\t\tlocal_id = -2;\n\t\t\t}\n\t\t\tfd = tcmd_vsock_make_any();\n\t\t\tif (fd >= 0 &&\n\t\t\t    tcmd_vsock_get_port(fd, &tsync_port) < 0) {\n\t\t\t\tclose(fd);\n\t\t\t\tfd = -1;\n\t\t\t}\n\t\t}\n\t}\n\ttrace_id = tracecmd_generate_traceid();\n\tret = tracecmd_msg_send_trace_resp(msg_handle, nr_cpus, page_size,\n\t\t\t\t\t   ports, use_fifos, trace_id,\n\t\t\t\t\t   tsync_proto, tsync_port);\n\tif (ret < 0)\n\t\tdie(\"Failed to send trace response\");\n\n\tif (tsync_proto) {\n\t\tfd = wait_for_connection(fd);\n\n\t\tif (rcid >= 0) {\n\t\t\ttsync = trace_tsync_as_host(fd, trace_id, 0, rcid,\n\t\t\t\t\t\t    client_cpus, tsync_proto,\n\t\t\t\t\t\t    get_clock(argc, argv));\n\t\t} else {\n\t\t\ttsync = trace_tsync_as_guest(fd, tsync_proto,\n\t\t\t\t\t\t     get_clock(argc, argv),\n\t\t\t\t\t\t     remote_id, local_id);\n\t\t}\n\t\tif (!tsync)\n\t\t\tclose(fd);\n\t}\n\n\ttrace_record_agent(msg_handle, nr_cpus, fds, argc, argv,\n\t\t\t   use_fifos, tsync, trace_id, rcid, network);\n\n\tif (tsync) {\n\t\tif (rcid < 0)\n\t\t\ttracecmd_tsync_with_host_stop(tsync);\n\t\ttracecmd_tsync_free(tsync);\n\t}\n\n\tif (tsync_protos) {\n\t\tfree(tsync_protos->names);\n\t\tfree(tsync_protos);\n\t}\n\tfree(argv[0]);\n\tfree(argv);\n\tfree(ports);\n\tfree(fds);\n\ttracecmd_msg_handle_close(msg_handle);\n\texit(0);\n}\n\nstatic volatile pid_t handler_pid;\n\nstatic void handle_sigchld(int sig)\n{\n\tint wstatus;\n\tpid_t pid;\n\n\tfor (;;) {\n\t\tpid = waitpid(-1, &wstatus, WNOHANG);\n\t\tif (pid <= 0)\n\t\t\tbreak;\n\n\t\tif (pid == handler_pid)\n\t\t\thandler_pid = 0;\n\t}\n}\n\nstatic pid_t do_fork()\n{\n\t/* in debug mode, we do not fork off children */\n\tif (tracecmd_get_debug())\n\t\treturn 0;\n\n\treturn fork();\n}\n\nstatic void agent_serve(unsigned int port, bool do_daemon, int proxy_id,\n\t\t\tconst char *network)\n{\n\tstruct sockaddr_storage net_addr;\n\tstruct sockaddr *addr = NULL;\n\tsocklen_t *addr_len_p = NULL;\n\tsocklen_t addr_len = sizeof(net_addr);\n\tint sd, cd, nr_cpus;\n\tunsigned int cid = -1, rcid = -1;\n\tpid_t pid;\n\n\tsignal(SIGCHLD, handle_sigchld);\n\n\tif (network) {\n\t\taddr = (struct sockaddr *)&net_addr;\n\t\taddr_len_p = &addr_len;\n\t}\n\n\tnr_cpus = tracecmd_count_cpus();\n\tpage_size = getpagesize();\n\n\tif (network) {\n\t\tsd = trace_net_make(port, USE_TCP);\n\t\tif (listen(sd, 5) < 0)\n\t\t\tdie(\"Failed to listen on %d\\n\", port);\n\t} else\n\t\tsd = tcmd_vsock_make(port);\n\tif (sd < 0)\n\t\tdie(\"Failed to open socket\");\n\ttracecmd_tsync_init();\n\n\tif (!network) {\n\t\tcid = tcmd_vsock_local_cid();\n\t\tif (cid >= 0)\n\t\t\tprintf(\"listening on @%u:%u\\n\", cid, port);\n\t}\n\n\tif (do_daemon && daemon(1, 0))\n\t\tdie(\"daemon\");\n\n\tfor (;;) {\n\t\tcd = accept(sd, addr, addr_len_p);\n\t\tif (cd < 0) {\n\t\t\tif (errno == EINTR)\n\t\t\t\tcontinue;\n\t\t\tdie(\"accept\");\n\t\t}\n\t\tif (proxy_id >= 0) {\n\t\t\t/* Only works with vsockets */\n\t\t\tif (get_vsocket_params(cd, NULL, &rcid) < 0) {\n\t\t\t\tdprint(\"Failed to find connected cid\");\n\t\t\t\tclose(cd);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (rcid != proxy_id) {\n\t\t\t\tdprint(\"Cid %d does not match expected cid %d\\n\",\n\t\t\t\t       rcid, proxy_id);\n\t\t\t\tclose(cd);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (tracecmd_get_debug())\n\t\t\ttrace_print_connection(cd, network);\n\n\t\tif (network && !trace_net_cmp_connection(&net_addr, network)) {\n\t\t\tdprint(\"Client does not match '%s'\\n\", network);\n\t\t\tclose(cd);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (handler_pid)\n\t\t\tgoto busy;\n\n\t\tpid = do_fork();\n\t\tif (pid == 0) {\n\t\t\tclose(sd);\n\t\t\tsignal(SIGCHLD, SIG_DFL);\n\t\t\tagent_handle(cd, nr_cpus, page_size, cid, rcid, network);\n\t\t}\n\t\tif (pid > 0)\n\t\t\thandler_pid = pid;\n\nbusy:\n\t\tclose(cd);\n\t}\n}\n\nenum {\n\tOPT_verbose\t= 254,\n\tOPT_debug\t= 255,\n\tOPT_notimeout\t= 256,\n};\n\nvoid trace_agent(int argc, char **argv)\n{\n\tstruct trace_guest *guest;\n\tbool do_daemon = false;\n\tunsigned int port = TRACE_AGENT_DEFAULT_PORT;\n\tconst char *network = NULL;\n\tint proxy_id = -1;\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"agent\") != 0)\n\t\tusage(argv);\n\n\tfor (;;) {\n\t\tint c, option_index = 0;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"port\", required_argument, NULL, 'p'},\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{\"debug\", no_argument, NULL, OPT_debug},\n\t\t\t{\"notimeout\", no_argument, NULL, OPT_notimeout},\n\t\t\t{\"verbose\", optional_argument, NULL, OPT_verbose},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long(argc-1, argv+1, \"+hp:DN:P:\",\n\t\t\t\tlong_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'N':\n\t\t\tnetwork = optarg;\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tport = atoi(optarg);\n\t\t\tif (proxy_id >= 0)\n\t\t\t\tdie(\"-N cannot be used with -P\");\n\t\t\tbreak;\n\t\tcase 'D':\n\t\t\tdo_daemon = true;\n\t\t\tbreak;\n\t\tcase 'P':\n\t\t\tproxy_id = atoi(optarg);\n\n\t\t\tguest = trace_get_guest(proxy_id, GUEST_NAME);\n\t\t\tif (!guest)\n\t\t\t\tdie(\"Failed to allocate guest instance\");\n\n\t\t\tbreak;\n\t\tcase OPT_debug:\n\t\t\ttracecmd_set_debug(true);\n\t\t\tbreak;\n\t\tcase OPT_notimeout:\n\t\t\ttracecmd_set_notimeout(true);\n\t\t\tbreak;\n\t\tcase OPT_verbose:\n\t\t\tif (trace_set_verbose(optarg) < 0)\n\t\t\t\tdie(\"invalid verbose level %s\", optarg);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif (optind < argc-1)\n\t\tusage(argv);\n\n\tagent_serve(port, do_daemon, proxy_id, network);\n}\n"
  },
  {
    "path": "tracecmd/trace-attach.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2023 Google Inc, Steven Rostedt <rostedt@goodmis.org>\n *\n */\n#include <stdlib.h>\n#include <ctype.h>\n#include <getopt.h>\n#include <errno.h>\n\n#include \"tracefs.h\"\n#include \"trace-local.h\"\n\nstruct timeshift_sample {\n\tstruct timeshift_sample *next;\n\tlong long\t\toffset;\n\tlong long\t\tscaling;\n\tlong long\t\ttimestamp;\n\tlong long\t\tfract;\n};\n\nstruct vcpu_pid {\n\tstruct vcpu_pid\t\t*next;\n\tint\t\t\tpid;\n\tint\t\t\tcpu;\n};\n\nstatic unsigned int num_cpus;\n\nstatic void *vcpu_pids;\n\nstatic struct timeshift_sample *tshifts;\nstatic struct timeshift_sample **tshifts_next = &tshifts;\n\nstatic u64 set_value(const char *str, const char *type, u64 def)\n{\n\tif (str && str[0] != '\\0' && str[0] != '-' && !isdigit(str[0]))\n\t\tdie(\"Bad %s value\", type);\n\n\tif (str && str[0])\n\t\treturn strtoull(str, NULL, 0);\n\n\treturn def;\n}\n\nstatic void add_timeshift(char *shift)\n{\n\tstruct timeshift_sample *tshift;\n\tchar *timestamp_str;\n\tchar *offset_str;\n\tchar *scale_str;\n\tchar *fract_str;\n\tchar *saveptr;\n\tu64 timestamp;\n\tu64 offset;\n\tu64 scale;\n\tu64 fract;\n\n\toffset_str = strparse(shift, ',', &saveptr);\n\tscale_str = strparse(NULL, ',', &saveptr);\n\tfract_str = strparse(NULL, ',', &saveptr);\n\ttimestamp_str = strparse(NULL, ',', &saveptr);\n\n\tif (!offset_str)\n\t\tdie(\"Bad timeshift argument\");\n\n\toffset = set_value(offset_str, \"offset\", 0);\n\tscale = set_value(scale_str, \"scaling\", 1);\n\tfract = set_value(fract_str, \"fraction\", 0);\n\ttimestamp = set_value(timestamp_str, \"timestamp\", 0);\n\n\ttshift = calloc(1, sizeof(*tshift));\n\tif (!tshift)\n\t\tdie(\"Could not allocate timeshift\");\n\n\t*tshifts_next = tshift;\n\ttshifts_next = &tshift->next;\n\n\ttshift->offset = offset;\n\ttshift->scaling = scale;\n\ttshift->fract = fract;\n\ttshift->timestamp = timestamp;\n}\n\nstatic void free_timeshifts(void)\n{\n\tstruct timeshift_sample *tshift;\n\n\twhile (tshifts) {\n\t\ttshift = tshifts;\n\t\ttshifts = tshift->next;\n\t\tfree(tshift);\n\t}\n}\n\nstatic void add_vcpu_pid(const char *pid)\n{\n\tstruct vcpu_pid *vpid;\n\n\tvpid = calloc(1, sizeof(*vpid));\n\tvpid->pid = atoi(pid);\n\tvpid->cpu = -1;\n\tvpid->next = vcpu_pids;\n\tvcpu_pids = vpid;\n}\n\nstatic void free_vcpu_pids(void)\n{\n\tstruct vcpu_pid *vpid;\n\n\twhile (vcpu_pids) {\n\t\tvpid = vcpu_pids;\n\t\tvcpu_pids = vpid->next;\n\t\tfree(vpid);\n\t}\n}\n\nstatic inline int test_vcpu_id(struct tep_format_field **vcpu_id_field,\n\t\t\t\tstruct tep_event *event, struct tep_record *record)\n{\n\tunsigned long long val;\n\tstruct vcpu_pid *vpid;\n\tbool done = true;\n\tint pid;\n\tint cnt = 0;\n\n\tif (!*vcpu_id_field) {\n\t\t*vcpu_id_field = tep_find_field(event, \"vcpu_id\");\n\t\t if (!*vcpu_id_field)\n\t\t\t die(\"Could not find vcpu_id field\");\n\t}\n\n\tpid = tep_data_pid(event->tep, record);\n\tfor (vpid = vcpu_pids; vpid; vpid = vpid->next) {\n\t\tif (vpid->cpu < 0) {\n\t\t\tdone = false;\n\t\t} else {\n\t\t\tcnt++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (vpid->pid == pid)\n\t\t\tbreak;\n\t}\n\n\tif (done || (num_cpus && cnt == num_cpus))\n\t\treturn -1;\n\n\tif (!vpid)\n\t\treturn 0;\n\n\tif (tep_read_number_field(*vcpu_id_field, record->data, &val))\n\t\tdie(\"Could not read data vcpu_id field\");\n\n\tvpid->cpu = (int)val;\n\n\treturn 0;\n}\n\nstatic int entry_callback(struct tracecmd_input *handle, struct tep_event *event,\n\t\t\t  struct tep_record *record, int cpu, void *data)\n{\n\tstatic struct tep_format_field *vcpu_id_field;\n\n\treturn test_vcpu_id(&vcpu_id_field, event, record);\n}\n\nstatic int exit_callback(struct tracecmd_input *handle, struct tep_event *event,\n\t\t\t  struct tep_record *record, int cpu, void *data)\n{\n\tstatic struct tep_format_field *vcpu_id_field;\n\n\treturn test_vcpu_id(&vcpu_id_field, event, record);\n}\n\nstatic int cmp_vcpus(const void *A, const void *B)\n{\n\tstruct vcpu_pid * const *a = A;\n\tstruct vcpu_pid * const *b = B;\n\n\tif ((*a)->cpu < (*b)->cpu)\n\t\treturn -1;\n\n\treturn (*a)->cpu > (*b)->cpu;\n}\n\nstatic void update_end(char **end, void *data, int size, const char *stop)\n{\n\tchar *str = *end;\n\n\tif (str + size > stop)\n\t\tdie(\"Error in calculating buffer size\");\n\n\tmemcpy(str, data, size);\n\t*end = str + size;\n}\n\nstatic void add_guest_to_host(struct tracecmd_output *host_ohandle,\n\t\t\t      struct tracecmd_input *guest_ihandle)\n{\n\tunsigned long long guest_id;\n\tstruct vcpu_pid **vcpu_list;\n\tstruct vcpu_pid *vpid;\n\tchar *name = \"\"; /* TODO, add name for guest */\n\tchar *stop;\n\tchar *buf;\n\tchar *end;\n\tint cpus = 0;\n\tint cpu;\n\tint size;\n\n\tguest_id = tracecmd_get_traceid(guest_ihandle);\n\n\tfor (vpid = vcpu_pids; vpid ; vpid = vpid->next) {\n\t\tif (vpid->cpu < 0)\n\t\t\tcontinue;\n\t\tcpus++;\n\t}\n\n\tvcpu_list = calloc(cpus, sizeof(*vcpu_list));\n\tif (!vcpu_list)\n\t\tdie(\"Could not allocate vCPU list\");\n\n\tcpus = 0;\n\tfor (vpid = vcpu_pids; vpid ; vpid = vpid->next) {\n\t\tif (vpid->cpu < 0)\n\t\t\tcontinue;\n\t\tvcpu_list[cpus++] = vpid;\n\t}\n\n\tqsort(vcpu_list, cpus, sizeof(*vcpu_list), cmp_vcpus);\n\n\tsize = strlen(name) + 1;\n\tsize += sizeof(int) + sizeof(long long);\n\tsize += cpus * (sizeof(int) * 2);\n\tbuf = calloc(1, size);\n\tif (!buf)\n\t\tdie(\"Failed allocation\");\n\n\tend = buf;\n\tstop = buf + size;\n\n\t/* TODO match endianess of existing file */\n\tupdate_end(&end, name, strlen(name) + 1, stop);\n\tupdate_end(&end, &guest_id, sizeof(guest_id), stop);\n\tupdate_end(&end, &cpus, sizeof(cpus), stop);\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tint vcpu = vcpu_list[cpu]->cpu;\n\t\tint pid = vcpu_list[cpu]->pid;\n\t\tupdate_end(&end, &cpu, sizeof(vcpu), stop);\n\t\tupdate_end(&end, &pid, sizeof(pid), stop);\n\t}\n\n\tif (tracecmd_add_option(host_ohandle, TRACECMD_OPTION_GUEST, size, buf) == NULL)\n\t\tdie(\"Failed to add GUEST option to host\");\n\n\tfree(vcpu_list);\n\tfree(buf);\n}\n\nstatic void add_timeshift_to_guest(struct tracecmd_output *guest_ohandle,\n\t\t\t\t   struct tracecmd_input *host_ihandle)\n{\n\tstruct timeshift_sample *tshift = tshifts;\n\tstruct timeshift_sample *last_tshift = NULL;\n\tunsigned long long host_id;\n\tchar *stop;\n\tchar *end;\n\tchar *buf;\n\tint proto;\n\tint size = 0;\n\tint cpus;\n\tint cpu;\n\n\thost_id = tracecmd_get_traceid(host_ihandle);\n\tcpus = num_cpus;\n\tproto = 0; /* For now we just have zero */\n\n\t/*\n\t * option size is:\n\t *   trace id:\t\t8 bytes\n\t *   protocol flags:\t4 bytes\n\t *   CPU count:\t\t4 bytes\n\t *\n\t * For each CPU:\n\t *   sample cnt:\t4 bytes\n\t *   list of times:\t8 bytes * sample cnt\n\t *   list of offsets:\t8 bytes * sample cnt\n\t *   list of scaling:\t8 bytes * sample cnt\n\t *\n\t * For each CPU:\n\t *    list of fract:\t8 bytes * CPU count\n\t */\n\tsize = 8 + 4 + 4;\n\n\t/* Include fraction bits here */\n\tsize += 8 * cpus;\n\n\t/* We only have one sample per CPU (for now) */\n\tsize += (4 + 8 * 3) * cpus;\n\n\tbuf = calloc(1, size);\n\tif (!buf)\n\t\tdie(\"Failed to allocate timeshift buffer\");\n\n\tend = buf;\n\tstop = buf + size;\n\tupdate_end(&end, &host_id, sizeof(host_id), stop);\n\tupdate_end(&end, &proto, sizeof(proto), stop);\n\tupdate_end(&end, &cpus, sizeof(cpus), stop);\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tstruct timeshift_sample *tsample = tshift;\n\t\tunsigned long long sample;\n\t\tint cnt = 1;\n\n\t\tif (!tsample)\n\t\t\ttsample = last_tshift;\n\n\t\tif (!tsample)\n\t\t\tdie(\"No samples given\");\n\n\t\tlast_tshift = tsample;\n\n\t\tupdate_end(&end, &cnt, sizeof(cnt), stop);\n\t\tsample = tsample->timestamp;\n\t\tupdate_end(&end, &sample, sizeof(sample), stop);\n\n\t\tsample = tsample->offset;\n\t\tupdate_end(&end, &sample, sizeof(sample), stop);\n\n\t\tsample = tsample->scaling;\n\t\tupdate_end(&end, &sample, sizeof(sample), stop);\n\t}\n\n\ttshift = tshifts;\n\tlast_tshift = NULL;\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tstruct timeshift_sample *tsample = tshift;\n\t\tunsigned long long sample;\n\n\t\tif (!tsample)\n\t\t\ttsample = last_tshift;\n\t\tlast_tshift = tsample;\n\n\t\tsample = tsample->fract;\n\n\t\tupdate_end(&end, &sample, sizeof(sample), stop);\n\t}\n\n\tif (tracecmd_add_option(guest_ohandle, TRACECMD_OPTION_TIME_SHIFT, size, buf) == NULL)\n\t\tdie(\"Failed to add TIME SHIFT option\");\n\n\tfree(buf);\n}\n\nstatic void add_tsc2nsec_to_guest(struct tracecmd_output *guest_ohandle,\n\t\t\t\t  struct tracecmd_input *host_ihandle)\n{\n\tunsigned long long offset;\n\tint mult;\n\tint shift;\n\tint ret;\n\tchar buf[sizeof(int) * 2 + sizeof(long long)];\n\tchar *stop;\n\tchar *end;\n\tint size = sizeof(buf);\n\n\tret = tracecmd_get_tsc2nsec(host_ihandle, &mult, &shift, &offset);\n\tif (ret < 0)\n\t\tdie(\"Host does not have tsc2nsec info\");\n\n\tend = buf;\n\tstop = buf + size;\n\tupdate_end(&end, &mult, sizeof(mult), stop);\n\tupdate_end(&end, &shift, sizeof(shift), stop);\n\tupdate_end(&end, &offset, sizeof(offset), stop);\n\n\tif (tracecmd_add_option(guest_ohandle, TRACECMD_OPTION_TSC2NSEC, size, buf) == NULL)\n\t\tdie(\"Failed to add TSC2NSEC option\");\n\n}\n\nstatic void map_cpus(struct tracecmd_input *handle)\n{\n\tint entry_ret;\n\tint exit_ret;\n\n\tentry_ret = tracecmd_follow_event(handle, \"kvm\", \"kvm_entry\", entry_callback, NULL);\n\texit_ret = tracecmd_follow_event(handle, \"kvm\", \"kvm_exit\", exit_callback, NULL);\n\n\tif (entry_ret < 0 && exit_ret < 0)\n\t\tdie(\"Host needs kvm_exit or kvm_entry events to attach\");\n\n\ttracecmd_iterate_events(handle, NULL, 0, NULL, NULL);\n}\n\nvoid trace_attach(int argc, char **argv)\n{\n\tstruct tracecmd_input *guest_ihandle;\n\tstruct tracecmd_input *host_ihandle;\n\tstruct tracecmd_output *guest_ohandle;\n\tstruct tracecmd_output *host_ohandle;\n\tunsigned long long guest_id;\n\tchar *guest_file;\n\tchar *host_file;\n\tint ret;\n\tint fd;\n\n\tfor (;;) {\n\t\tint c;\n\n\t\tc = getopt(argc-1, argv+1, \"c:s:h\");\n\t\tif (c == -1)\n\t\t\tbreak;\n\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tadd_timeshift(optarg);\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\tnum_cpus = atoi(optarg);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\t/* Account for \"attach\" */\n\toptind++;\n\n\tif ((argc - optind) < 3)\n\t\tusage(argv);\n\n\thost_file = argv[optind++];\n\tguest_file = argv[optind++];\n\n\tfor (; optind < argc; optind++)\n\t\tadd_vcpu_pid(argv[optind]);\n\n\n\thost_ihandle = tracecmd_open(host_file,TRACECMD_FL_LOAD_NO_PLUGINS );\n\tguest_ihandle = tracecmd_open(guest_file,TRACECMD_FL_LOAD_NO_PLUGINS );\n\n\tif (!host_ihandle)\n\t\tdie(\"Could not read %s\\n\", host_file);\n\n\tif (!guest_ihandle)\n\t\tdie(\"Could not read %s\\n\", guest_file);\n\n\tguest_id = tracecmd_get_traceid(guest_ihandle);\n\tif (!guest_id)\n\t\tdie(\"Guest data file does not contain traceid\");\n\n\tmap_cpus(host_ihandle);\n\n\tret = tracecmd_get_guest_cpumap(host_ihandle, guest_id,\n\t\t\t\t\tNULL, NULL, NULL);\n\tif (ret == 0) {\n\t\tprintf(\"Guest is already mapped in host (id=0x%llx) .. skipping ...\\n\",\n\t\t       guest_id);\n\t} else {\n\n\t\tfd = open(host_file, O_RDWR);\n\t\tif (fd < 0)\n\t\t\tdie(\"Could not write %s\", host_file);\n\n\t\thost_ohandle = tracecmd_get_output_handle_fd(fd);\n\t\tif (!host_ohandle)\n\t\t\tdie(\"Error setting up %s for write\", host_file);\n\n\t\tadd_guest_to_host(host_ohandle, guest_ihandle);\n\t\ttracecmd_output_close(host_ohandle);\n\t}\n\n\tfd = open(guest_file, O_RDWR);\n\tif (fd < 0)\n\t\tdie(\"Could not write %s\", guest_file);\n\n\tguest_ohandle = tracecmd_get_output_handle_fd(fd);\n\tif (!guest_ohandle)\n\t\tdie(\"Error setting up %s for write\", guest_file);\n\n\tadd_timeshift_to_guest(guest_ohandle, host_ihandle);\n\tadd_tsc2nsec_to_guest(guest_ohandle, host_ihandle);\n\n\ttracecmd_output_close(guest_ohandle);\n\n\ttracecmd_close(guest_ihandle);\n\ttracecmd_close(host_ihandle);\n\n\tfree_timeshifts();\n\tfree_vcpu_pids();\n\n\treturn;\n}\n"
  },
  {
    "path": "tracecmd/trace-check-events.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdlib.h>\n#include <getopt.h>\n#include <errno.h>\n\n#include \"tracefs.h\"\n#include \"trace-local.h\"\n\nenum {\n\tOPT_verbose\t= 255,\n};\n\nvoid trace_check_events(int argc, char **argv)\n{\n\tconst char *tracing;\n\tint ret, c;\n\tint parsing_failures = 0;\n\tstruct tep_handle *pevent = NULL;\n\tstruct tep_plugin_list *list = NULL;\n\tint open_flags = 0;\n\tint option_index = 0;\n\tstatic struct option long_options[] = {\n\t\t{\"verbose\", optional_argument, NULL, OPT_verbose},\n\t\t{NULL, 0, NULL, 0}\n\t};\n\n\n\twhile ((c = getopt_long(argc-1, argv+1, \"+hN\", long_options, &option_index)) >= 0) {\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'N':\n\t\t\topen_flags |= TRACECMD_FL_LOAD_NO_PLUGINS;\n\t\t\tbreak;\n\t\tcase OPT_verbose:\n\t\t\tif (trace_set_verbose(optarg) < 0)\n\t\t\t\tdie(\"invalid verbose level %s\", optarg);\n\t\t\tbreak;\n\t\t}\n\t}\n\ttracing = tracefs_tracing_dir();\n\n\tif (!tracing) {\n\t\tprintf(\"Can not find or mount tracing directory!\\n\"\n\t\t       \"Either tracing is not configured for this \"\n\t\t       \"kernel\\n\"\n\t\t       \"or you do not have the proper permissions to \"\n\t\t       \"mount the directory\");\n\t\texit(EINVAL);\n\t}\n\n\tpevent = tep_alloc();\n\tif (!pevent)\n\t\texit(EINVAL);\n\n\tlist = tcmd_load_plugins(pevent, open_flags);\n\tret = tracefs_fill_local_events(tracing, pevent, &parsing_failures);\n\tif (ret || parsing_failures)\n\t\tret = EINVAL;\n\ttep_unload_plugins(list, pevent);\n\ttep_free(pevent);\n\n\treturn;\n}\n"
  },
  {
    "path": "tracecmd/trace-clear.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n * Updates:\n * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n *\n */\n#include <stdlib.h>\n#include <unistd.h>\n#include <getopt.h>\n\n#include \"tracefs.h\"\n#include \"trace-local.h\"\n\nstruct instances_list {\n\tstruct instances_list *next;\n\tstruct tracefs_instance *instance;\n};\n\nstatic int add_new_instance(struct instances_list **list, char *name)\n{\n\tstruct instances_list *new;\n\n\tif (!tracefs_instance_exists(name))\n\t\treturn -1;\n\tnew = calloc(1, sizeof(*new));\n\tif (!new)\n\t\treturn -1;\n\tnew->instance = tracefs_instance_create(name);\n\tif (!new->instance) {\n\t\tfree(new);\n\t\treturn -1;\n\t}\n\n\tnew->next = *list;\n\t*list = new;\n\treturn 0;\n}\n\nstatic int add_instance_walk(const char *name, void *data)\n{\n\treturn add_new_instance((struct instances_list **)data, (char *)name);\n}\n\nstatic void clear_list(struct instances_list *list)\n{\n\tstruct instances_list *del;\n\n\twhile (list) {\n\t\tdel = list;\n\t\tlist = list->next;\n\t\ttracefs_instance_free(del->instance);\n\t\tfree(del);\n\t}\n}\n\nstatic void clear_instance_trace(struct tracefs_instance *instance)\n{\n\tFILE *fp;\n\tchar *path;\n\n\t/* reset the trace */\n\tpath = tracefs_instance_get_file(instance, \"trace\");\n\tfp = fopen(path, \"w\");\n\tif (!fp)\n\t\tdie(\"writing to '%s'\", path);\n\ttracefs_put_tracing_file(path);\n\tfwrite(\"0\", 1, 1, fp);\n\tfclose(fp);\n}\n\nstatic void clear_trace(struct instances_list *instances)\n{\n\tif (instances) {\n\t\twhile (instances) {\n\t\t\tclear_instance_trace(instances->instance);\n\t\t\tinstances = instances->next;\n\t\t}\n\t} else\n\t\tclear_instance_trace(NULL);\n}\n\nvoid trace_clear(int argc, char **argv)\n{\n\tstruct instances_list *instances = NULL;\n\tbool all = false;\n\tint c;\n\n\tfor (;;) {\n\t\tint option_index = 0;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"all\", no_argument, NULL, 'a'},\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long (argc-1, argv+1, \"+haB:\",\n\t\t\t\t long_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'B':\n\t\t\tif (add_new_instance(&instances, optarg))\n\t\t\t\tdie(\"Failed to allocate instance %s\", optarg);\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tall = true;\n\t\t\tif (tracefs_instances_walk(add_instance_walk, &instances))\n\t\t\t\tdie(\"Failed to add all instances\");\n\t\t\tbreak;\n\t\tcase 'h':\n\t\tcase '?':\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tclear_trace(instances);\n\tif (all)\n\t\tclear_trace(NULL);\n\tclear_list(instances);\n\texit(0);\n}\n"
  },
  {
    "path": "tracecmd/trace-cmd.bash",
    "content": "make_small() {\n    local w=$1\n\n    echo $w | tr A-Z a-z\n}\n\nshow_instances()\n{\n   local cur=\"$1\"\n   local bufs=$(trace-cmd list -B)\n   if [ \"$bufs\" == \"No buffer instances defined\" ]; then\n\treturn 0\n   fi\n   COMPREPLY=( $(compgen -W \"${bufs}\" -- \"${cur}\") )\n   return 0\n}\n\nshow_virt()\n{\n    local cur=\"$1\"\n    if ! which virsh &>/dev/null; then\n\treturn 1\n    fi\n    local virt=`virsh list | awk '/^ *[0-9]/ { print $2 }'`\n    COMPREPLY=( $(compgen -W \"${virt}\" -- \"${cur}\") )\n    return 0\n}\n\nshow_options()\n{\n   local cur=\"$1\"\n   local options=$(trace-cmd list -o | sed -e 's/^\\(no\\)*\\(.*\\)/\\2 no\\2/')\n   COMPREPLY=( $(compgen -W \"${options}\" -- \"${cur}\") )\n   return 0\n}\n\n__show_files()\n{\n    COMPREPLY=( $(compgen -f -- \"$cur\") )\n    if [ ${#COMPREPLY[@]} -gt 1 ]; then\n\t    return 0;\n    fi\n    # directories get '/' instead of space\n    DIRS=( $(compgen -d -- \"$cur\"))\n    if [ ${#DIRS[@]} -eq 1 ]; then\n\tcompopt -o nospace\n\tCOMPREPLY=\"$DIRS/\"\n\treturn 0;\n    fi\n    return 0\n}\n\ncmd_options()\n{\n    local type=\"$1\"\n    local cur=\"$2\"\n    local extra=\"$3\"\n    local cmds=$(trace-cmd $type -h 2>/dev/null|grep \"^ *-\" | \\\n\t\t\t\t sed -e 's/ *\\(-[^ ]*\\).*/\\1/')\n    COMPREPLY=( $(compgen -W \"${cmds} ${extra}\" -- \"${cur}\") )\n}\n\ncmd_options_files()\n{\n    cmd_options \"$1\" \"$2\" \"$3\"\n    if [ ${#COMPREPLY[@]} -eq 0 ]; then\n\t__show_files \"${cur}\"\n    fi\n}\n\nplugin_options()\n{\n    local cur=\"$1\"\n\n    local opts=$(trace-cmd list -O | sed -ne 's/option://p')\n    COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n}\n\ncompression_param()\n{\n    local opts=$(trace-cmd list -c | grep -v 'Supported' | cut -d \",\" -f1)\n    opts+=\" any none \"\n    COMPREPLY=( $(compgen -W \"${opts}\") )\n}\n\nlist_events() {\n    local cur=$1\n\n    local list=$(trace-cmd list -e \"$cur\")\n    local prefix=${cur%%:*}\n    if [ -z \"$cur\" -o  \"$cur\" != \"$prefix\" ]; then\n\techo \"${list}\"\n    else\n\tlocal events=$(for e in $list; do echo ${e/*:/}; done | sort -u)\n\tlocal systems=$(for s in $list; do echo ${s/:*/:}; done | sort -u)\n\n\techo \"${events} ${systems}\"\n    fi\n}\n\n__trace_cmd_list_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    case \"$prev\" in\n\tlist)\n\t    local cmds=$(trace-cmd list -h |egrep \"^ {10}-\" | \\\n\t\t\t\t sed -e 's/^ *\\(-[^ ]*\\).*/\\1/')\n\t    COMPREPLY=( $(compgen -W \"${cmds}\" -- \"${cur}\") )\n\t    ;;\n\t-e)\n\t    local list=`list_events \"$cur\"`\n\t    COMPREPLY=( $(compgen -W \"all $list\" -- \"${cur}\") )\n\t    ;;\n\t*)\n\t    size=${#words[@]}\n\t    if [ $size -gt 3 ]; then\n\t\tif [ \"$cur\" == \"-\" ]; then\n\t\t    let size=$size-3\n\t\telse\n\t\t    let size=$size-2\n\t\tfi\n\t\tlocal w=\"${words[$size]}\"\n\t\tif [ \"$w\" == \"-e\" ]; then\n\t\t    local cmds=$(trace-cmd list -h |egrep \"^ {12}-\" | \\\n\t\t\t\t sed -e 's/.*\\(-.\\).*/\\1/')\n\t\t    COMPREPLY=( $(compgen -W \"${cmds}\" -- \"${cur}\") )\n\t\tfi\n\t    fi\n\t    ;;\n    esac\n}\n\n__trace_cmd_show_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    case \"$prev\" in\n\t-B)\n\t    show_instances \"$cur\"\n\t    ;;\n\t--hist|--trigger)\n\t    local list=`list_events $cur`\n\t    COMPREPLY=( $(compgen -W \"all ${list}\" -- \"${cur}\") )\n\t    ;;\n\t*)\n\t    cmd_options show \"$cur\"\n\t    ;;\n    esac\n}\n\n__trace_cmd_extract_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    case \"$prev\" in\n\textract)\n\t    cmd_options \"$prev\" \"$cur\"\n\t    ;;\n\t-B)\n\t    show_instances \"$cur\"\n\t    ;;\n\t*)\n\t    __show_files\n\t    ;;\n    esac\n}\n\n__trace_cmd_record_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    case \"$prev\" in\n\t-e)\n\t    local list=`list_events $cur`\n\t    COMPREPLY=( $(compgen -W \"all ${list}\" -- \"${cur}\") )\n\n            # This is still to handle the \"*:*\" special case\n            if [[ -n \"$prefix\" ]]; then\n                local reply_n=${#COMPREPLY[*]}\n                for (( i = 0; i < $reply_n; i++)); do\n                    COMPREPLY[$i]=${COMPREPLY[i]##${prefix}:}\n                done\n            fi\n            ;;\n        -p)\n            local plugins=$(trace-cmd list -p)\n\t    COMPREPLY=( $(compgen -W \"${plugins}\" -- \"${cur}\" ) )\n            ;;\n        -l|-n|-g)\n            # This is extremely slow still (may take >1sec).\n            local funcs=$(trace-cmd list -f | sed 's/ .*//')\n            COMPREPLY=( $(compgen -W \"${funcs}\" -- \"${cur}\") )\n            ;;\n\t-B)\n\t    show_instances \"$cur\"\n\t    ;;\n\t-O)\n\t    show_options \"$cur\"\n\t    ;;\n\t-A)\n\t    if ! show_virt \"$cur\"; then\n\t\tcmd_options_files record \"$cur\"\n\t    fi\n\t    ;;\n\t--compression)\n\t    compression_param\n\t    ;;\n        *)\n\t    # stream start and profile do not show all options\n\t    cmd_options_files record \"$cur\"\n\t    ;;\n    esac\n}\n\n__trace_cmd_report_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    case \"$prev\" in\n\t-O)\n\t    plugin_options \"$cur\"\n\t    ;;\n        *)\n\t    cmd_options_files report \"$cur\"\n\t    ;;\n    esac\n}\n\ndynevent_options()\n{\n    local cur=\"$1\"\n    local opts=(\"kprobe\" \"kretprobe\" \"uprobe\" \"uretprobe\" \"eprobe\" \"synth\" \"all\")\n    COMPREPLY=( $(compgen -W \"${opts[*]}\" -- \"${cur}\") )\n}\n\n__trace_cmd_reset_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    case \"$prev\" in\n        -B)\n            show_instances \"$cur\"\n            ;;\n        -k)\n            dynevent_options \"$cur\"\n            ;;\n        *)\n            cmd_options reset \"$cur\"\n            ;;\n    esac\n}\n\n__trace_cmd_dump_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    case \"$prev\" in\n\t-i)\n\t    __show_files\n\t    ;;\n\t*)\n\t    cmd_options dump \"$cur\"\n\t    ;;\n    esac\n}\n\n__trace_cmd_convert_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    case \"$prev\" in\n\t-i)\n\t    __show_files\n\t    ;;\n\t-o)\n\t    __show_files\n\t    ;;\n\t--compression)\n\t    compression_param\n\t    ;;\n\t*)\n\t    cmd_options convert \"$cur\"\n\t    ;;\n    esac\n}\n\n##### SQLHIST COMMANDS #####\n\nprev_keyword() {\n    local i=$1\n    shift\n    local words=(\"$@\")\n\n    while [ $i -gt 0 ]; do\n\tlet i=$i-1\n\tlocal w=`make_small ${words[$i]}`\n\n\tcase $w in\n\t    select)\n\t\t      echo \"select\"\n\t\t      return\n\t\t      ;;\n\t\t  from)\n\t\t      echo \"from\"\n\t\t      return\n\t\t      ;;\n\t\t  as)\n\t\t      echo \"as\"\n\t\t      return\n\t\t      ;;\n\t\t  on)\n\t\t      echo \"on\"\n\t\t      return\n\t\t      ;;\n\t\t  join)\n\t\t      echo \"join\"\n\t\t      return\n\t\t      ;;\n\t\t  where)\n\t\t      echo \"where\"\n\t\t      return\n\t\t      ;;\n\t\t  *)\n\t\t      if [ \"$w\" != \"${w%%,}\" ]; then\n\t\t\t  echo \",\"\n\t\t\t  return\n\t\t      fi\n\t\t      if [ \"$w\" != \"${w%%=}\" ]; then\n\t\t\t  echo \"=\"\n\t\t\t  return\n\t\t      fi\n\t\t      ;;\n\t    esac\n\tdone\n\t    echo \"\"\n}\n\nprev_command() {\n    local i=$1\n    shift\n    local words=(\"$@\")\n\n    while [ $i -gt 0 ]; do\n\tlet i=$i-1\n\tlocal w=`make_small ${words[$i]}`\n\n\tcase $w in\n\t    select)\n\t\t      echo \"select\"\n\t\t      return\n\t\t      ;;\n\t\t  from)\n\t\t      echo \"from\"\n\t\t      return\n\t\t      ;;\n\t\t  on)\n\t\t      echo \"on\"\n\t\t      return\n\t\t      ;;\n\t\t  join)\n\t\t      echo \"join\"\n\t\t      return\n\t\t      ;;\n\t\t  where)\n\t\t      echo \"where\"\n\t\t      return\n\t\t      ;;\n\t    esac\n\tdone\n\t    echo \"\"\n}\n\nadd_vars() {\n    local words=(\"$@\")\n\n    local i=$COMP_CWORD\n\n    let found_from=0\n\n    while [ $i -gt 0 ]; do\n\tlet i=$i-1\n\tlocal w=`make_small ${words[$i]}`\n\n\tcase $w in\n\t    \"from\")\n\t\tlet found_from=1\n\t\t;;\n\t    *)\n\t\tif [ $found_from ]; then\n\t\t    start=`echo $w | sed -e 's/\\.[^\\.]*$//'`\n\t\t    if [ \"$start\" != \"$w\" -a \"$start\" == \"${start%%\\.*}\" ]; then\n\t\t\techo -n \"$start \"\n\t\t    fi\n\t\tfi\n\t\t;;\n\tesac\n    done\n}\n\nadd_options() {\n    local cur=\"$1\"\n    local list=\"$2\"\n\n    COMPREPLY=( $(compgen -W \"${list}\" -- \"${cur}\") )\n}\n\nprint_fields() {\n    local event=$1\n    local var=$2\n    local extra=$3\n\n    local list=`trace-cmd list -e \"^${event/\\./:}\\$\" -F |  cut -d';' -f1 | sed -ne 's/\\t.*:.* \\(.*\\)/\\1/p' |sed -e 's/\\[.*\\]//'`\n\n    for field in $list $extra; do\n\techo \"$event.$field\"\n\tif [ ! -z \"$var\" ]; then\n\t    echo \"$var.$field\"\n\tfi\n    done\n}\n\nselect_options() {\n    local cur=$1\n    local extra=$2\n    local list=`list_events \"${cur/\\./:}\" | sed -e 's/:/./g'`\n    local select_list=\" TIMESTAMP_DELTA TIMESTAMP_DELTA_USECS $extra\"\n    local select_fields=\" TIMESTAMP TIMESTAMP_USECS STACKTRACE\"\n    add_options \"$cur\" \"$list $select_list\"\n    local cnt=${#COMPREPLY[@]}\n    if [ $cnt -eq 1 ]; then\n\tlocal comp=${COMPREPLY[0]}\n\tlocal w=$(compgen -W \"$select_list\" -- \"$comp\" )\n\tif [ -z \"$w\" ]; then\n\t    COMPREPLY=(\"$comp.\")\n\t    compopt -o nospace\n\tfi\n    elif [ $cnt -eq 0 ]; then\n\tlocal w=`echo $cur | sed -e 's/\\.[^\\.]*$//'`\n\tlist=`print_fields $w \"\" \"$select_fields\"`\n\tCOMPREPLY=( $(compgen -W \"${list}\" -- \"${cur}\") )\n    fi\n}\n\ncheck_as() {\n    local words=(\"$@\")\n\n    last_key=`prev_keyword $COMP_CWORD ${words[@]}`\n    if [ \"$last_key\" != \"as\" ]; then\n\techo -n \"AS\"\n    fi\n}\n\non_list() {\n    local type=$1\n    shift\n    local words=(\"$@\")\n\n    local i=$COMP_CWORD\n\n    local var=\"\"\n\n    while [ $i -gt 0 ]; do\n\tlet i=$i-1\n\tlocal w=`make_small ${words[$i]}`\n\tcase $w in\n\t    \"from\"|\"join\")\n\t\tif [ $w == $type ]; then\n\t\t    print_fields ${words[$i+1]} \"$var\"\n\t\t    return\n\t\tfi\n\t\tvar=\"\"\n\t\t;;\n\t    as)\n\t\tvar=${words[$i+1]}\n\t\t;;\n\tesac\n    done\n}\n\nupdate_completion() {\n    local cur=$1\n    shift\n    local words=(\"$@\")\n\n    if [ ${#COMPREPLY[@]} -gt 0 ]; then\n\treturn\n    fi\n\n    for w in ${words[@]}; do\n\tif [ \"$w\" != \"${w##$cur}\" ]; then\n\t    COMPREPLY=(\"$w\")\n\t    return\n\tfi\n    done\n}\n\n__trace_cmd_sqlhist_complete()\n{\n    local prev=$1\n    local cur=$2\n    shift 2\n    local words=(\"$@\")\n\n    if [ \"$cur\" != \"${cur%%,}\" ]; then\n\tCOMPREPLY=(\"$cur\")\n\treturn\n    fi\n\n    local p=`make_small $prev`\n\n    if [ \"$p\" != \"${p%%,}\" ]; then\n\tp=`prev_command $COMP_CWORD ${words[@]}`\n    fi\n\n    case \"$p\" in\n\t\"sqlhist\")\n\t    cmd_options sqlhist \"$cur\" \"SELECT\"\n\t    update_completion \"$cur\" select\n\t    ;;\n\t\"select\")\n\t    select_options \"$cur\"\n\t    ;;\n\t\"on\")\n\t    list=`on_list \"from\" ${words[@]}`\n\t    add_options \"$cur\" \"$list\"\n\t    ;;\n\t\"as\")\n\t    local last_cmd=`prev_command $COMP_CWORD ${words[@]}`\n\t    case $last_cmd in\n\t\t\"from\"|\"join\")\n\t\t    list=`add_vars ${words[@]}`\n\t\t    if [ ! -z \"$list\" ]; then\n\t\t\tadd_options \"$cur\" \"$list\"\n\t\t    fi\n\t\t    ;;\n\t    esac\n\t    ;;\n\t\"from\"|\"join\")\n\t    local list=$(trace-cmd list -e \"${cur/\\./:}\" | tr : .)\n\t    local prefix=${cur/\\./}\n\t    if [ -z \"$cur\" -o  \"$cur\" != \"$prefix\" ]; then\n\t\tCOMPREPLY=( $(compgen -W \"${list}\" -- \"${cur}\") )\n\t    else\n\t\tlocal events=$(for e in $list; do echo ${e/*\\./}; done | sort -u)\n\t        local systems=$(for s in $list; do echo ${s/\\.*/.}; done | sort -u)\n\n\t\tCOMPREPLY=( $(compgen -W \"all ${events} ${systems}\" -- \"${cur}\") )\n\t    fi\n\t    ;;\n\t*)\n\t    local last_cmd=`prev_command $COMP_CWORD ${words[@]}`\n\t    local list=`check_as ${words[@]}`\n\t    local alist=\"\"\n\t    if [ ! -z \"$list\" ]; then\n\t\talist=\"as\"\n\t    fi\n\t    case $last_cmd in\n\t\t\"select\")\n\t\t    if [ \"$cur\" != \"${cur%%,}\" ]; then\n\t\t\tselect_options \"$cur\" \"$list\"\n\t\t    else\n\t\t\tadd_options \"$cur\" \"FROM , $list\"\n\t\t\tupdate_completion \"$cur\" from $alist\n\t\t    fi\n\t\t    ;;\n\t\t\"from\")\n\t\t    add_options \"$cur\" \"JOIN $list\"\n\t\t    update_completion \"$cur\" join $alist\n\t\t    ;;\n\t\t\"join\")\n\t\t    add_options \"$cur\" \"ON $list\"\n\t\t    update_completion \"$cur\" on $alist\n\t\t    ;;\n\t\t\"on\")\n\t\t    if [ \"$cur\" != \"${cur%%=}\" ]; then\n\t\t\tCOMPREPLY=(\"\")\n\t\t    else\n\t\t\tlast_key=`prev_keyword $COMP_CWORD ${words[@]}`\n\t\t\tif [ \"$last_key\" == \"=\" ]; then\n\t\t\t    if [ $prev == \"=\" ]; then\n\t\t\t\tlist=`on_list \"join\" ${words[@]}`\n\t\t\t\tadd_options \"$cur\" \"$list\"\n\t\t\t    else\n\t\t\t\tadd_options \"$cur\" \"WHERE\"\n\t\t\t\tupdate_completion \"$cur\" where\n\t\t\t    fi\n\t\t\telse\n\t\t\t    add_options \"$cur\" \"=\"\n\t\t\tfi\n\t\t    fi\n\t\t    ;;\n\t\t\"where\")\n\t\t    ;;\n\t\t*)\n\t\t    cmd_options sqlhist \"$cur\" \"SELECT\"\n\t\t    update_completion \"$cur\" select\n\t\t    ;;\n\t    esac\n\t    ;;\n    esac\n}\n\n##### SQLHIST COMMANDS END #####\n\n__show_command_options()\n{\n    local command=\"$1\"\n    local prev=\"$2\"\n    local cur=\"$3\"\n    local cmds=( $(trace-cmd --help 2>/dev/null | \\\n\t\t    grep \" - \" | sed 's/^ *//; s/ -.*//') )\n\n    for cmd in ${cmds[@]}; do\n\tif [ $cmd == \"$command\" ]; then\n\t    local opts=$(trace-cmd $cmd -h 2>/dev/null|grep \"^ *-\" | \\\n\t\t\t\t sed -e 's/ *\\(-[^ ]*\\).*/\\1/')\n\t    if [ \"$prev\" == \"-B\" ]; then\n\t\tfor opt in ${opts[@]}; do\n\t\t    if [ \"$opt\" == \"-B\" ]; then\n\t\t\tshow_instances \"$cur\"\n\t\t\treturn 0\n\t\t    fi\n\t\tdone\n\t    fi\n\t    COMPREPLY=( $(compgen -W \"${opts}\" -- \"$cur\"))\n\t    break\n\tfi\n    done\n    if [ ${#COMPREPLY[@]} -eq 0 ]; then\n\t__show_files \"${cur}\"\n    fi\n}\n\n_trace_cmd_complete()\n{\n    local cur=\"\"\n    local prev=\"\"\n    local words=()\n\n    # Not to use COMP_WORDS to avoid buggy behavior of Bash when\n    # handling with words including \":\", like:\n    #\n    # prev=\"${COMP_WORDS[COMP_CWORD-1]}\"\n    # cur=\"${COMP_WORDS[COMP_CWORD]}\"\n    #\n    # Instead, we use _get_comp_words_by_ref() magic.\n    _get_comp_words_by_ref -n : cur prev words\n\n    if [ \"$prev\" == \"trace-cmd\" ]; then\n            local cmds=$(trace-cmd --help 2>/dev/null | \\\n                                grep \" - \" | sed 's/^ *//; s/ -.*//')\n            COMPREPLY=( $(compgen -W \"${cmds}\" -- \"${cur}\") )\n\t    return;\n    fi\n\n    local w=\"${words[1]}\"\n\n    case \"$w\" in\n\tlist)\n\t    __trace_cmd_list_complete \"${prev}\" \"${cur}\" ${words[@]}\n\t    return 0\n\t    ;;\n\tshow)\n\t    __trace_cmd_show_complete \"${prev}\" \"${cur}\" ${words[@]}\n\t    return 0\n\t    ;;\n\textract)\n\t    __trace_cmd_extract_complete \"${prev}\" \"${cur}\" ${words[@]}\n\t    return 0\n\t    ;;\n\trecord|stream|start|set|profile)\n\t    __trace_cmd_record_complete \"${prev}\" \"${cur}\" ${words[@]}\n\t    return 0\n\t    ;;\n\treport)\n\t    __trace_cmd_report_complete \"${prev}\" \"${cur}\" ${words[@]}\n\t    return 0\n\t    ;;\n\treset)\n\t    __trace_cmd_reset_complete \"${prev}\" \"${cur}\" \"${words[@]}\"\n\t    return 0\n\t    ;;\n\tdump)\n\t    __trace_cmd_dump_complete \"${prev}\" \"${cur}\" ${words[@]}\n\t    return 0\n\t    ;;\n\tconvert)\n\t    __trace_cmd_convert_complete \"${prev}\" \"${cur}\" ${words[@]}\n\t    return 0\n\t    ;;\n\tsqlhist)\n\t    __trace_cmd_sqlhist_complete \"${prev}\" \"${cur}\" ${words[@]}\n\t    return 0\n\t    ;;\n        *)\n\t    __show_command_options \"$w\" \"${prev}\" \"${cur}\"\n            ;;\n    esac\n}\ncomplete -F _trace_cmd_complete trace-cmd\n"
  },
  {
    "path": "tracecmd/trace-cmd.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include <string.h>\n#include <getopt.h>\n#include <unistd.h>\n#include <dirent.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <sys/syscall.h>\n\n#include \"trace-local.h\"\n\nint silence_warnings;\nint show_status;\n\n#ifndef gettid\n#define gettid() syscall(__NR_gettid)\n#endif\n\nvoid warning(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (silence_warnings)\n\t\treturn;\n\n\tif (errno)\n\t\tperror(\"trace-cmd\");\n\terrno = 0;\n\n\tva_start(ap, fmt);\n\tfprintf(stderr, \"  \");\n\tvfprintf(stderr, fmt, ap);\n\tva_end(ap);\n\n\tfprintf(stderr, \"\\n\");\n}\n\nvoid *malloc_or_die(unsigned int size)\n{\n\tvoid *data;\n\n\tdata = malloc(size);\n\tif (!data)\n\t\tdie(\"malloc\");\n\treturn data;\n}\n\n/* Same as strtok_r(), but allows empty tokens */\nchar *strparse(char *str, char delim, char **save)\n{\n\tchar *next;\n\n\tif (!str) {\n\t\tstr = *save;\n\t\tif ((*save)[0] == '\\0')\n\t\t\treturn NULL;\n\t}\n\n\tnext = strchr(str, delim);\n\tif (next) {\n\t\t*next = '\\0';\n\t\t*save = next + 1;\n\t} else {\n\t\t*save = str + strlen(str);\n\t}\n\treturn str;\n}\n\nvoid tracecmd_debug(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!tracecmd_get_debug())\n\t\treturn;\n\n\tva_start(ap, fmt);\n\tprintf(\"[%d] \", (int)gettid());\n\tvprintf(fmt, ap);\n\tva_end(ap);\n}\n\nstatic struct trace_log_severity {\n\tint\t\tid;\n\tconst char\t*name;\n} log_severity[] = {\n\t{ .id = TEP_LOG_NONE, .name = \"none\" },\n\t{ .id = TEP_LOG_CRITICAL, .name = \"crit\" },\n\t{ .id = TEP_LOG_ERROR, .name = \"err\" },\n\t{ .id = TEP_LOG_WARNING, .name = \"warn\" },\n\t{ .id = TEP_LOG_INFO, .name = \"info\" },\n\t{ .id = TEP_LOG_DEBUG, .name = \"debug\" },\n\t{ .id = TEP_LOG_ALL, .name = \"all\" },\n};\n\nvoid trace_set_loglevel(int level)\n{\n\ttracecmd_set_loglevel(level);\n\ttracefs_set_loglevel(level);\n\ttep_set_loglevel(level);\n}\n\nint trace_set_verbose(char *level)\n{\n\tint id;\n\n\t/* Default level is info */\n\tif (!level)\n\t\tlevel = \"info\";\n\n\tif (isdigit(level[0])) {\n\t\tid = atoi(level);\n\t\tif (id >= TEP_LOG_NONE) {\n\t\t\tif (id > TEP_LOG_ALL)\n\t\t\t\tid = TEP_LOG_ALL;\n\t\t\ttrace_set_loglevel(id);\n\t\t\treturn 0;\n\t\t}\n\t} else {\n\t\tint size = ARRAY_SIZE(log_severity);\n\t\tint i;\n\n\t\tfor (i = 0; i < size; i++) {\n\t\t\tif (!strncmp(level, log_severity[i].name, strlen(log_severity[i].name))) {\n\t\t\t\ttrace_set_loglevel(log_severity[i].id);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n/**\n * struct command\n * @name command name\n * @run function to execute on command `name`\n */\nstruct command {\n\tchar *name;\n\tvoid (*run)(int argc, char **argv);\n};\n\n\n/**\n * Lookup table that maps command names to functions\n */\nstruct command commands[] = {\n\t{\"report\", trace_report},\n\t{\"snapshot\", trace_snapshot},\n\t{\"hist\", trace_hist},\n\t{\"mem\", trace_mem},\n\t{\"listen\", trace_listen},\n\t{\"agent\", trace_agent},\n\t{\"setup-guest\", trace_setup_guest},\n\t{\"split\", trace_split},\n\t{\"restore\", trace_restore},\n\t{\"stack\", trace_stack},\n\t{\"check-events\", trace_check_events},\n\t{\"record\", trace_record},\n\t{\"start\", trace_start},\n\t{\"set\", trace_set},\n\t{\"extract\", trace_extract},\n\t{\"stop\", trace_stop},\n\t{\"stream\", trace_stream},\n\t{\"profile\", trace_profile},\n\t{\"restart\", trace_restart},\n\t{\"clear\", trace_clear},\n\t{\"reset\", trace_reset},\n\t{\"stat\", trace_stat},\n\t{\"options\", trace_option},\n\t{\"show\", trace_show},\n\t{\"list\", trace_list},\n\t{\"help\", trace_usage},\n\t{\"dump\", trace_dump},\n\t{\"attach\", trace_attach},\n\t{\"convert\", trace_convert},\n\t{\"sqlhist\", trace_sqlhist},\n\t{\"-h\", trace_usage},\n};\n\nint main (int argc, char **argv)\n{\n\tint i;\n\n\terrno = 0;\n\n\tif (argc < 2)\n\t\ttrace_usage(argc, argv);\n\n\tfor (i = 0; i < ARRAY_SIZE(commands); ++i) {\n\t\tif (strcmp(argv[1], commands[i].name) == 0 ){\n\t\t\tcommands[i].run(argc, argv);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\t/* No valid command found, show help */\n\ttrace_usage(argc, argv);\nout:\n\texit(0);\n}\n"
  },
  {
    "path": "tracecmd/trace-convert.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n */\n#include <stdlib.h>\n#include <unistd.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n\n#include \"trace-local.h\"\n#include \"trace-cmd.h\"\n#include \"trace-cmd-private.h\"\n\nstatic void convert_file(const char *in, const char *out, int file_version, char *compr)\n{\n\tstruct tracecmd_input *ihandle;\n\tstruct tracecmd_output *ohandle;\n\n\tihandle = tracecmd_open_head(in, 0);\n\tif (!ihandle)\n\t\tdie(\"error reading %s\", in);\n\n\tohandle = tracecmd_copy(ihandle, out, TRACECMD_FILE_CPU_FLYRECORD, file_version, compr);\n\tif (!ohandle)\n\t\tdie(\"error writing %s\", out);\n\n\ttracecmd_output_close(ohandle);\n\ttracecmd_close(ihandle);\n}\n\nenum {\n\tOPT_file_version\t= 254,\n\tOPT_compression\t\t= 255,\n};\n\nvoid trace_convert(int argc, char **argv)\n{\n\tchar *input_file = NULL;\n\tchar *output_file = NULL;\n\tchar *compression = NULL;\n\tint file_version = tracecmd_default_file_version();\n\tint c;\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"convert\") != 0)\n\t\tusage(argv);\n\n\tfor (;;) {\n\t\tint option_index = 0;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"compression\", required_argument, NULL, OPT_compression},\n\t\t\t{\"file-version\", required_argument, NULL, OPT_file_version},\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long (argc-1, argv+1, \"+hi:o:\", long_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'i':\n\t\t\tif (input_file)\n\t\t\t\tdie(\"Only one input file is supported, %s already set\",\n\t\t\t\t    input_file);\n\t\t\tinput_file = optarg;\n\t\t\tbreak;\n\t\tcase 'o':\n\t\t\tif (output_file)\n\t\t\t\tdie(\"Only one output file is supported, %s already set\",\n\t\t\t\t    output_file);\n\t\t\toutput_file = optarg;\n\t\t\tbreak;\n\t\tcase OPT_compression:\n\t\t\tif (strcmp(optarg, \"any\") && strcmp(optarg, \"none\") &&\n\t\t\t    !tracecmd_compress_is_supported(optarg, NULL))\n\t\t\t\tdie(\"Compression algorithm  %s is not supported\", optarg);\n\t\t\tcompression = optarg;\n\t\t\tbreak;\n\t\tcase OPT_file_version:\n\t\t\tfile_version = atoi(optarg);\n\t\t\tif (file_version < FILE_VERSION_MIN || file_version > FILE_VERSION_MAX)\n\t\t\t\tdie(\"Unsupported file version %d, \"\n\t\t\t\t    \"supported versions are from %d to %d\",\n\t\t\t\t    file_version, FILE_VERSION_MIN, FILE_VERSION_MAX);\n\n\t\t\tbreak;\n\t\tcase 'h':\n\t\tcase '?':\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif ((argc - optind) >= 2) {\n\t\tif (output_file)\n\t\t\tusage(argv);\n\t\toutput_file = argv[optind + 1];\n\t}\n\n\tif (!input_file)\n\t\tinput_file = DEFAULT_INPUT_FILE;\n\tif (!output_file)\n\t\tusage(argv);\n\tif (file_version >= FILE_VERSION_COMPRESSION && !compression)\n\t\tcompression = \"any\";\n\n\tconvert_file(input_file, output_file, file_version, compression);\n}\n"
  },
  {
    "path": "tracecmd/trace-dump.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n * Updates:\n * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n */\n#include <stdlib.h>\n#include <unistd.h>\n#include <getopt.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <errno.h>\n\n#include \"trace-local.h\"\n\n#define TRACING_STR\t\"tracing\"\n#define HEAD_PAGE_STR\t\"header_page\"\n#define HEAD_PAGE_EVENT\t\"header_event\"\n#define HEAD_OPTIONS\t\"options  \"\n#define HEAD_LATENCY\t\"latency  \"\n#define HEAD_FLYRECORD\t\"flyrecord\"\n\n#define DUMP_SIZE\t1024\n\nstatic struct tep_handle *tep;\nstatic unsigned int trace_cpus;\nstatic int has_clock;\nstatic unsigned long file_version;\nstatic bool\tread_compress;\nstatic struct tracecmd_compression *compress;\nstatic char *meta_strings;\nstatic int meta_strings_size;\n\nenum dump_items {\n\tSUMMARY\t\t= (1 << 0),\n\tHEAD_PAGE\t= (1 << 1),\n\tHEAD_EVENT\t= (1 << 2),\n\tFTRACE_FORMAT\t= (1 << 3),\n\tEVENT_SYSTEMS\t= (1 << 4),\n\tEVENT_FORMAT\t= (1 << 5),\n\tKALLSYMS\t= (1 << 6),\n\tTRACE_PRINTK\t= (1 << 7),\n\tCMDLINES\t= (1 << 8),\n\tOPTIONS\t\t= (1 << 9),\n\tFLYRECORD\t= (1 << 10),\n\tCLOCK\t\t= (1 << 11),\n\tSECTIONS\t= (1 << 12),\n\tSTRINGS\t\t= (1 << 13),\n\tLAST_BOOT_INFO\t= (1 << 14),\n\tMODULES_FILE\t= (1 << 15),\n};\n\nstruct file_section {\n\tint id;\n\tunsigned long long offset;\n\tstruct file_section *next;\n\tenum dump_items verbosity;\n};\n\nstatic struct file_section *sections;\n\nenum dump_items verbosity;\n\n#define DUMP_CHECK(X) ((X) & verbosity)\n\n#define do_print(ids, fmt, ...)\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\t\\\n\t\tif (!(ids) || DUMP_CHECK(ids))\t\t\t\\\n\t\t\ttracecmd_plog(fmt, ##__VA_ARGS__);\t\\\n\t} while (0)\n\nstatic int read_fd(int fd, char *dst, int len)\n{\n\tsize_t size = 0;\n\tint r;\n\n\tdo {\n\t\tr = read(fd, dst+size, len);\n\t\tif (r > 0) {\n\t\t\tsize += r;\n\t\t\tlen -= r;\n\t\t} else\n\t\t\tbreak;\n\t} while (r > 0);\n\n\tif (len)\n\t\treturn -1;\n\treturn size;\n}\n\nstatic int read_compressed(int fd, char *dst, int len)\n{\n\n\tif (read_compress)\n\t\treturn tracecmd_compress_buffer_read(compress, dst, len);\n\n\treturn read_fd(fd, dst, len);\n}\n\nstatic int do_lseek(int fd, int offset, int whence)\n{\n\tif (read_compress)\n\t\treturn tracecmd_compress_lseek(compress, offset, whence);\n\n\treturn lseek(fd, offset, whence);\n}\n\nstatic int read_file_string(int fd, char *dst, int len)\n{\n\tsize_t size = 0;\n\tint r;\n\n\tdo {\n\t\tr = read_compressed(fd, dst+size, 1);\n\t\tif (r > 0) {\n\t\t\tsize++;\n\t\t\tlen--;\n\t\t} else\n\t\t\tbreak;\n\t\tif (!dst[size - 1])\n\t\t\tbreak;\n\t} while (r > 0 && len);\n\n\tif (!size || dst[size - 1])\n\t\treturn -1;\n\treturn 0;\n}\n\nstatic int read_file_bytes(int fd, char *dst, int len)\n{\n\tint ret;\n\n\tret = read_compressed(fd, dst, len);\n\treturn ret < 0 ? ret : 0;\n}\n\nstatic void read_dump_string(int fd, int size, enum dump_items id)\n{\n\tchar buf[DUMP_SIZE];\n\tint lsize;\n\n\twhile (size) {\n\t\tlsize = (size < DUMP_SIZE) ? size : DUMP_SIZE - 1;\n\t\tif (read_file_bytes(fd, buf, lsize))\n\t\t\tdie(\"cannot read %d bytes\", lsize);\n\t\tbuf[lsize] = 0;\n\t\tdo_print(id, \"%s\", buf);\n\t\tsize -= lsize;\n\t}\n\n\tdo_print(id, \"\\n\");\n}\n\nstatic int read_file_number(int fd, void *digit, int size)\n{\n\tunsigned long long val;\n\tchar buf[8];\n\n\tif (size > 8)\n\t\treturn -1;\n\n\tif (read_file_bytes(fd, buf, size))\n\t\treturn -1;\n\n\tval = tep_read_number(tep, buf, size);\n\tswitch (size) {\n\tcase 1:\n\t\t*((char *)digit) = val;\n\t\tbreak;\n\tcase 2:\n\t\t*((unsigned short *)digit) = val;\n\t\tbreak;\n\tcase 4:\n\t\t*((unsigned int *)digit) = val;\n\t\tbreak;\n\tcase 8:\n\t\t*((unsigned long long *)digit) = val;\n\t\tbreak;\n\tdefault:\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nstatic const char *get_metadata_string(int offset)\n{\n\tif (!meta_strings || offset < 0 || meta_strings_size <= offset)\n\t\treturn NULL;\n\n\treturn meta_strings + offset;\n}\n\nstatic void dump_initial_format(int fd)\n{\n\tchar magic[] = TRACECMD_MAGIC;\n\tchar buf[DUMP_SIZE];\n\tint val4;\n\n\tdo_print(SUMMARY, \"\\t[Initial format]\\n\");\n\n\t/* check initial bytes */\n\tif (read_file_bytes(fd, buf, sizeof(magic)))\n\t\tdie(\"cannot read %zu bytes magic\", sizeof(magic));\n\tif (memcmp(buf, magic, sizeof(magic)) != 0)\n\t\tdie(\"wrong file magic\");\n\n\t/* check initial tracing string */\n\tif (read_file_bytes(fd, buf, strlen(TRACING_STR)))\n\t\tdie(\"cannot read %zu bytes tracing string\", strlen(TRACING_STR));\n\tbuf[strlen(TRACING_STR)] = 0;\n\tif (strncmp(buf, TRACING_STR, strlen(TRACING_STR)) != 0)\n\t\tdie(\"wrong tracing string: %s\", buf);\n\n\t/* get file version */\n\tif (read_file_string(fd, buf, DUMP_SIZE))\n\t\tdie(\"no version string\");\n\n\tdo_print(SUMMARY, \"\\t\\t%s\\t[Version]\\n\", buf);\n\tfile_version = strtol(buf, NULL, 10);\n\tif (!file_version && errno)\n\t\tdie(\"Invalid file version string %s\", buf);\n\tif (!tracecmd_is_version_supported(file_version))\n\t\tdie(\"Unsupported file version %lu\", file_version);\n\n\t/* get file endianness*/\n\tif (read_file_bytes(fd, buf, 1))\n\t\tdie(\"cannot read file endianness\");\n\tdo_print(SUMMARY, \"\\t\\t%d\\t[%s endian]\\n\", buf[0], buf[0]?\"Big\":\"Little\");\n\n\ttep_set_file_bigendian(tep, buf[0]);\n\ttep_set_local_bigendian(tep, tracecmd_host_bigendian());\n\n\t/* get file bytes per long*/\n\tif (read_file_bytes(fd, buf, 1))\n\t\tdie(\"cannot read file bytes per long\");\n\tdo_print(SUMMARY, \"\\t\\t%d\\t[Bytes in a long]\\n\", buf[0]);\n\n\tif (read_file_number(fd, &val4, 4))\n\t\tdie(\"cannot read file page size\");\n\tdo_print(SUMMARY, \"\\t\\t%d\\t[Page size, bytes]\\n\", val4);\n}\n\nstatic void dump_compress(int fd)\n{\n\tchar zname[DUMP_SIZE];\n\tchar zver[DUMP_SIZE];\n\n\tif (file_version < FILE_VERSION_COMPRESSION)\n\t\treturn;\n\n\t/* get compression header */\n\tif (read_file_string(fd, zname, DUMP_SIZE))\n\t\tdie(\"no compression header\");\n\n\tif (read_file_string(fd, zver, DUMP_SIZE))\n\t\tdie(\"no compression version\");\n\n\tdo_print((SUMMARY), \"\\t\\t%s\\t[Compression algorithm]\\n\", zname);\n\tdo_print((SUMMARY), \"\\t\\t%s\\t[Compression version]\\n\", zver);\n\n\tif (strcmp(zname, \"none\")) {\n\t\tcompress = tracecmd_compress_alloc(zname, zver, fd, tep, NULL);\n\t\tif (!compress)\n\t\t\tdie(\"cannot uncompress the file\");\n\t}\n}\n\nstatic void dump_header_page(int fd)\n{\n\tunsigned long long size;\n\tchar buf[DUMP_SIZE];\n\n\tdo_print((SUMMARY | HEAD_PAGE), \"\\t[Header page, \");\n\n\t/* check header string */\n\tif (read_file_bytes(fd, buf, strlen(HEAD_PAGE_STR) + 1))\n\t\tdie(\"cannot read %zu bytes header string\", strlen(HEAD_PAGE_STR));\n\tif (strncmp(buf, HEAD_PAGE_STR, strlen(HEAD_PAGE_STR)) != 0)\n\t\tdie(\"wrong header string: %s\", buf);\n\n\tif (read_file_number(fd, &size, 8))\n\t\tdie(\"cannot read the size of the page header information\");\n\n\tdo_print((SUMMARY | HEAD_PAGE), \"%lld bytes]\\n\", size);\n\n\tread_dump_string(fd, size, HEAD_PAGE);\n}\n\nstatic void dump_header_event(int fd)\n{\n\tunsigned long long size;\n\tchar buf[DUMP_SIZE];\n\n\tdo_print((SUMMARY | HEAD_EVENT), \"\\t[Header event, \");\n\n\t/* check header string */\n\tif (read_file_bytes(fd, buf, strlen(HEAD_PAGE_EVENT) + 1))\n\t\tdie(\"cannot read %zu bytes header string\", strlen(HEAD_PAGE_EVENT));\n\tif (strncmp(buf, HEAD_PAGE_EVENT, strlen(HEAD_PAGE_EVENT)) != 0)\n\t\tdie(\"wrong header string: %s\", buf);\n\n\tif (read_file_number(fd, &size, 8))\n\t\tdie(\"cannot read the size of the page header information\");\n\n\tdo_print((SUMMARY | HEAD_EVENT), \"%lld bytes]\\n\", size);\n\n\tread_dump_string(fd, size, HEAD_EVENT);\n}\n\nstatic void uncompress_reset(void)\n{\n\tif (compress && file_version >= FILE_VERSION_COMPRESSION) {\n\t\tread_compress = false;\n\t\ttracecmd_compress_reset(compress);\n\t}\n}\n\nstatic int uncompress_block(void)\n{\n\tint ret = 0;\n\n\tif (compress && file_version >= FILE_VERSION_COMPRESSION) {\n\t\tret = tracecmd_uncompress_block(compress);\n\t\tif (!ret)\n\t\t\tread_compress = true;\n\n\t}\n\n\treturn ret;\n}\n\nstatic void dump_ftrace_events_format(int fd)\n{\n\tunsigned long long size;\n\tunsigned int count;\n\n\tdo_print((SUMMARY | FTRACE_FORMAT), \"\\t[Ftrace format, \");\n\tif (read_file_number(fd, &count, 4))\n\t\tdie(\"cannot read the count of the ftrace events\");\n\n\tdo_print((SUMMARY | FTRACE_FORMAT), \"%d events]\\n\", count);\n\n\twhile (count) {\n\t\tif (read_file_number(fd, &size, 8))\n\t\t\tdie(\"cannot read the size of the %d ftrace event\", count);\n\t\tread_dump_string(fd, size, FTRACE_FORMAT);\n\t\tcount--;\n\t}\n}\n\nstatic void dump_events_format(int fd)\n{\n\tunsigned long long size;\n\tunsigned int systems;\n\tunsigned int events;\n\tchar buf[DUMP_SIZE];\n\n\tdo_print((SUMMARY | EVENT_FORMAT | EVENT_SYSTEMS), \"\\t[Events format, \");\n\n\tif (read_file_number(fd, &systems, 4))\n\t\tdie(\"cannot read the count of the event systems\");\n\n\tdo_print((SUMMARY | EVENT_FORMAT | EVENT_SYSTEMS), \"%d systems]\\n\", systems);\n\n\twhile (systems) {\n\n\t\tif (read_file_string(fd, buf, DUMP_SIZE))\n\t\t\tdie(\"cannot read the name of the %dth system\", systems);\n\t\tif (read_file_number(fd, &events, 4))\n\t\t\tdie(\"cannot read the count of the events in system %s\",\n\t\t\t     buf);\n\t\tdo_print(EVENT_SYSTEMS, \"\\t\\t%s %d [system, events]\\n\", buf, events);\n\t\twhile (events) {\n\t\t\tif (read_file_number(fd, &size, 8))\n\t\t\t\tdie(\"cannot read the format size of the %dth event from system %s\",\n\t\t\t\t    events, buf);\n\t\t\tread_dump_string(fd, size, EVENT_FORMAT);\n\t\t\tevents--;\n\t\t}\n\t\tsystems--;\n\t}\n}\n\nstatic void dump_kallsyms(int fd)\n{\n\tunsigned int size;\n\n\tdo_print((SUMMARY | KALLSYMS), \"\\t[Kallsyms, \");\n\n\tif (read_file_number(fd, &size, 4))\n\t\tdie(\"cannot read the size of the kallsyms\");\n\n\tdo_print((SUMMARY | KALLSYMS), \"%d bytes]\\n\", size);\n\n\tread_dump_string(fd, size, KALLSYMS);\n}\n\nstatic void dump_printk(int fd)\n{\n\tunsigned int size;\n\n\tdo_print((SUMMARY | TRACE_PRINTK), \"\\t[Trace printk, \");\n\n\tif (read_file_number(fd, &size, 4))\n\t\tdie(\"cannot read the size of the trace printk\");\n\n\tdo_print((SUMMARY | TRACE_PRINTK), \"%d bytes]\\n\", size);\n\n\tread_dump_string(fd, size, TRACE_PRINTK);\n}\n\nstatic void dump_cmdlines(int fd)\n{\n\tunsigned long long size;\n\n\tdo_print((SUMMARY | CMDLINES), \"\\t[Saved command lines, \");\n\n\tif (read_file_number(fd, &size, 8))\n\t\tdie(\"cannot read the size of the saved command lines\");\n\n\tdo_print((SUMMARY | CMDLINES), \"%d bytes]\\n\", size);\n\n\tread_dump_string(fd, size, CMDLINES);\n}\n\nstatic void dump_modules(int fd)\n{\n\tunsigned int size = 0;\n\tchar buf[1024];\n\tint len;\n\n\tdo_print((SUMMARY | MODULES_FILE), \"\\t[Saved modules]\\n\");\n\n\twhile ((len = read_compressed(fd, buf, 1024)) > 0) {\n\t\tdo_print((SUMMARY | MODULES_FILE), \"%*s\", len, buf);\n\t\tsize += len;\n\t}\n\n\tdo_print((SUMMARY | MODULES_FILE), \"\\n[%d bytes]\\n\", size);\n}\n\nstatic void dump_cpus_count(int fd)\n{\n\tif (read_file_number(fd, &trace_cpus, 4))\n\t\tdie(\"cannot read the cpu count\");\n\n\tdo_print(SUMMARY, \"\\t%d [CPUs with tracing data]\\n\", trace_cpus);\n}\n\nstatic void dump_option_string(int fd, int size, char *desc)\n{\n\tdo_print(OPTIONS, \"\\t\\t[Option %s, %d bytes]\\n\", desc, size);\n\tif (size)\n\t\tread_dump_string(fd, size, OPTIONS);\n}\n\nstatic void dump_section_header(int fd, enum dump_items v, unsigned short *flags)\n{\n\tunsigned long long offset, size;\n\tunsigned short fl;\n\tunsigned short id;\n\tconst char *desc;\n\tint desc_id;\n\n\toffset = lseek(fd, 0, SEEK_CUR);\n\tif (read_file_number(fd, &id, 2))\n\t\tdie(\"cannot read the section id\");\n\n\tif (read_file_number(fd, &fl, 2))\n\t\tdie(\"cannot read the section flags\");\n\n\tif (read_file_number(fd, &desc_id, 4))\n\t\tdie(\"no section description\");\n\n\tdesc = get_metadata_string(desc_id);\n\tif (!desc)\n\t\tdesc = \"Unknown\";\n\n\tif (read_file_number(fd, &size, 8))\n\t\tdie(\"cannot read section size\");\n\n\tdo_print(v, \"\\t[Section %d @ %lld: \\\"%s\\\", flags 0x%X, %lld bytes]\\n\",\n\t\t id, offset, desc, fl, size);\n\n\tif (flags)\n\t\t*flags = fl;\n}\n\nstatic void dump_option_buffer(int fd, unsigned short option, int size)\n{\n\tunsigned long long total_size = 0;\n\tunsigned long long data_size;\n\tunsigned long long current;\n\tunsigned long long offset;\n\tunsigned short flags;\n\tchar clock[DUMP_SIZE];\n\tchar name[DUMP_SIZE];\n\tint page_size;\n\tint cpus = 0;\n\tint id;\n\tint i;\n\n\tif (size < 8)\n\t\tdie(\"broken buffer option with size %d\", size);\n\n\tif (read_file_number(fd, &offset, 8))\n\t\tdie(\"cannot read the offset of the buffer option\");\n\n\tif (read_file_string(fd, name, DUMP_SIZE))\n\t\tdie(\"cannot read the name of the buffer option\");\n\n\tif (file_version < FILE_VERSION_SECTIONS) {\n\t\tdo_print(OPTIONS|FLYRECORD, \"\\t\\t[Option BUFFER, %d bytes]\\n\", size);\n\t\tdo_print(OPTIONS|FLYRECORD, \"%lld [offset]\\n\", offset);\n\t\tdo_print(OPTIONS|FLYRECORD, \"\\\"%s\\\" [name]\\n\", name);\n\t\treturn;\n\t}\n\n\tcurrent = lseek(fd, 0, SEEK_CUR);\n\tif (lseek(fd, offset, SEEK_SET) == (off_t)-1)\n\t\tdie(\"cannot goto buffer offset %lld\", offset);\n\n\tdump_section_header(fd, FLYRECORD, &flags);\n\n\tif (lseek(fd, current, SEEK_SET) == (off_t)-1)\n\t\tdie(\"cannot go back to buffer option\");\n\n\tdo_print(OPTIONS|FLYRECORD, \"\\t\\t[Option BUFFER, %d bytes]\\n\", size);\n\tdo_print(OPTIONS|FLYRECORD, \"%lld [offset]\\n\", offset);\n\tdo_print(OPTIONS|FLYRECORD, \"\\\"%s\\\" [name]\\n\", name);\n\n\tif (read_file_string(fd, clock, DUMP_SIZE))\n\t\tdie(\"cannot read clock of the buffer option\");\n\n\tdo_print(OPTIONS|FLYRECORD, \"\\\"%s\\\" [clock]\\n\", clock);\n\tif (option == TRACECMD_OPTION_BUFFER) {\n\t\tif (read_file_number(fd, &page_size, 4))\n\t\t\tdie(\"cannot read the page size of the buffer option\");\n\t\tdo_print(OPTIONS|FLYRECORD, \"%d [Page size, bytes]\\n\", page_size);\n\n\t\tif (read_file_number(fd, &cpus, 4))\n\t\t\tdie(\"cannot read the cpu count of the buffer option\");\n\n\t\tdo_print(OPTIONS|FLYRECORD, \"%d [CPUs]:\\n\", cpus);\n\t\tfor (i = 0; i < cpus; i++) {\n\t\t\tif (read_file_number(fd, &id, 4))\n\t\t\t\tdie(\"cannot read the id of cpu %d from the buffer option\", i);\n\n\t\t\tif (read_file_number(fd, &offset, 8))\n\t\t\t\tdie(\"cannot read the offset of cpu %d from the buffer option\", i);\n\n\t\t\tif (read_file_number(fd, &data_size, 8))\n\t\t\t\tdie(\"cannot read the data size of cpu %d from the buffer option\", i);\n\n\t\t\ttotal_size += data_size;\n\t\t\tdo_print(OPTIONS|FLYRECORD, \"   %d %lld\\t%lld\\t[id, data offset and size]\\n\",\n\t\t\t\t id, offset, data_size);\n\t\t}\n\t\tdo_print(SUMMARY, \"\\t\\[buffer \\\"%s\\\", \\\"%s\\\" clock, %d page size, \"\n\t\t\t \"%d cpus, %lld bytes flyrecord data]\\n\",\n\t\t\t name, clock, page_size, cpus, total_size);\n\t} else {\n\t\tdo_print(SUMMARY, \"\\t\\[buffer \\\"%s\\\", \\\"%s\\\" clock, latency data]\\n\", name, clock);\n\t}\n\n}\n\nstatic void dump_option_int(int fd, int size, char *desc)\n{\n\tint val;\n\n\tdo_print(OPTIONS, \"\\t\\t[Option %s, %d bytes]\\n\", desc, size);\n\tread_file_number(fd, &val, size);\n\tdo_print(OPTIONS, \"%d\\n\", val);\n}\n\nstatic void dump_option_xlong(int fd, int size, char *desc)\n{\n\tlong long val;\n\n\tdo_print(OPTIONS, \"\\t\\t[Option %s, %d bytes]\\n\", desc, size);\n\tread_file_number(fd, &val, size);\n\tdo_print(OPTIONS, \"0x%llX\\n\", val);\n}\n\nstruct time_shift_cpu {\n\tunsigned int count;\n\tlong long *scalings;\n\tlong long *frac;\n\tlong long *offsets;\n\tunsigned long long *times;\n};\n\nstatic void dump_option_timeshift(int fd, int size)\n{\n\tstruct time_shift_cpu *cpus_data;\n\tlong long trace_id;\n\tunsigned int flags;\n\tunsigned int cpus;\n\tint i, j;\n\n\t/*\n\t * long long int (8 bytes) trace session ID\n\t * int (4 bytes) count of timestamp offsets.\n\t * long long array of size [count] of times,\n\t *      when the offsets were calculated.\n\t * long long array of size [count] of timestamp offsets.\n\t */\n\tif (size < 12) {\n\t\tdo_print(OPTIONS, \"Broken time shift option, size %s\", size);\n\t\treturn;\n\t}\n\tdo_print(OPTIONS, \"\\t\\t[Option TimeShift, %d bytes]\\n\", size);\n\tread_file_number(fd, &trace_id, 8);\n\tsize -= 8;\n\tdo_print(OPTIONS, \"0x%llX [peer's trace id]\\n\", trace_id);\n\tread_file_number(fd, &flags, 4);\n\tsize -= 4;\n\tdo_print(OPTIONS, \"0x%llX [peer's protocol flags]\\n\", flags);\n\tread_file_number(fd, &cpus, 4);\n\tsize -= 4;\n\tdo_print(OPTIONS, \"0x%llX [peer's CPU count]\\n\", cpus);\n\tcpus_data = calloc(cpus, sizeof(struct time_shift_cpu));\n\tif (!cpus_data)\n\t\treturn;\n\tfor (j = 0; j < cpus; j++) {\n\t\tif (size < 4)\n\t\t\tgoto out;\n\t\tread_file_number(fd, &cpus_data[j].count, 4);\n\t\tsize -= 4;\n\t\tdo_print(OPTIONS, \"%lld [samples count for CPU %d]\\n\", cpus_data[j].count, j);\n\t\tcpus_data[j].times = calloc(cpus_data[j].count, sizeof(long long));\n\t\tcpus_data[j].offsets = calloc(cpus_data[j].count, sizeof(long long));\n\t\tcpus_data[j].scalings = calloc(cpus_data[j].count, sizeof(long long));\n\t\tcpus_data[j].frac = calloc(cpus_data[j].count, sizeof(long long));\n\t\tif (!cpus_data[j].times || !cpus_data[j].offsets ||\n\t\t    !cpus_data[j].scalings || !cpus_data[j].frac)\n\t\t\tgoto out;\n\t\tfor (i = 0; i < cpus_data[j].count; i++) {\n\t\t\tif (size < 8)\n\t\t\t\tgoto out;\n\t\t\tread_file_number(fd, cpus_data[j].times + i, 8);\n\t\t\tsize -= 8;\n\t\t}\n\t\tfor (i = 0; i < cpus_data[j].count; i++) {\n\t\t\tif (size < 8)\n\t\t\t\tgoto out;\n\t\t\tread_file_number(fd, cpus_data[j].offsets + i, 8);\n\t\t\tsize -= 8;\n\t\t}\n\t\tfor (i = 0; i < cpus_data[j].count; i++) {\n\t\t\tif (size < 8)\n\t\t\t\tgoto out;\n\t\t\tread_file_number(fd, cpus_data[j].scalings + i, 8);\n\t\t\tsize -= 8;\n\t\t}\n\t}\n\n\tif (size > 0) {\n\t\tfor (j = 0; j < cpus; j++) {\n\t\t\tif (!cpus_data[j].frac)\n\t\t\t\tgoto out;\n\t\t\tfor (i = 0; i < cpus_data[j].count; i++) {\n\t\t\t\tif (size < 8)\n\t\t\t\t\tgoto out;\n\t\t\t\tread_file_number(fd, cpus_data[j].frac + i, 8);\n\t\t\t\tsize -= 8;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (j = 0; j < cpus; j++) {\n\t\tfor (i = 0; i < cpus_data[j].count; i++)\n\t\t\tdo_print(OPTIONS, \"\\t%lld %lld %llu %llu[offset * scaling >> fraction @ time]\\n\",\n\t\t\t\t cpus_data[j].offsets[i], cpus_data[j].scalings[i],\n\t\t\t\t cpus_data[j].frac[i], cpus_data[j].times[i]);\n\n\t}\n\nout:\n\tif (j < cpus)\n\t\tdo_print(OPTIONS, \"Broken time shift option\\n\");\n\tfor (j = 0; j < cpus; j++) {\n\t\tfree(cpus_data[j].times);\n\t\tfree(cpus_data[j].offsets);\n\t\tfree(cpus_data[j].scalings);\n\t\tfree(cpus_data[j].frac);\n\t}\n\tfree(cpus_data);\n}\n\nvoid dump_option_guest(int fd, int size)\n{\n\tunsigned long long trace_id;\n\tchar *buf, *p;\n\tint cpu, pid;\n\tint cpus;\n\tint i;\n\n\tdo_print(OPTIONS, \"\\t\\t[Option GUEST, %d bytes]\\n\", size);\n\n\t/*\n\t * Guest name, null terminated string\n\t * long long (8 bytes) trace-id\n\t * int (4 bytes) number of guest CPUs\n\t * array of size number of guest CPUs:\n\t *\tint (4 bytes) Guest CPU id\n\t *\tint (4 bytes) Host PID, running the guest CPU\n\t */\n\tbuf = calloc(1, size);\n\tif (!buf)\n\t\treturn;\n\tif (read_file_bytes(fd, buf, size))\n\t\tgoto out;\n\n\tp = buf;\n\tdo_print(OPTIONS, \"%s [Guest name]\\n\", p);\n\tsize -= strlen(buf) + 1;\n\tp += strlen(buf) + 1;\n\n\tif (size < sizeof(long long))\n\t\tgoto out;\n\ttrace_id = tep_read_number(tep, p, sizeof(long long));\n\tsize -= sizeof(long long);\n\tp += sizeof(long long);\n\tdo_print(OPTIONS, \"0x%llX [trace id]\\n\", trace_id);\n\n\tif (size < sizeof(int))\n\t\tgoto out;\n\tcpus = tep_read_number(tep, p, sizeof(int));\n\tsize -= sizeof(int);\n\tp += sizeof(int);\n\tdo_print(OPTIONS, \"%d [Guest CPUs]\\n\", cpus);\n\n\tfor (i = 0; i < cpus; i++) {\n\t\tif (size < 2 * sizeof(int))\n\t\t\tgoto out;\n\t\tcpu = tep_read_number(tep, p, sizeof(int));\n\t\tsize -= sizeof(int);\n\t\tp += sizeof(int);\n\t\tpid = tep_read_number(tep, p, sizeof(int));\n\t\tsize -= sizeof(int);\n\t\tp += sizeof(int);\n\t\tdo_print(OPTIONS, \"  %d %d [guest cpu, host pid]\\n\", cpu, pid);\n\t}\n\nout:\n\tfree(buf);\n}\n\nvoid dump_option_tsc2nsec(int fd, int size)\n{\n\tint mult, shift;\n\tunsigned long long offset;\n\n\tdo_print(OPTIONS, \"\\n\\t\\t[Option TSC2NSEC, %d bytes]\\n\", size);\n\n\tif (read_file_number(fd, &mult, 4))\n\t\tdie(\"cannot read tsc2nsec multiplier\");\n\tif (read_file_number(fd, &shift, 4))\n\t\tdie(\"cannot read tsc2nsec shift\");\n\tif (read_file_number(fd, &offset, 8))\n\t\tdie(\"cannot read tsc2nsec offset\");\n\tdo_print(OPTIONS, \"%d %d %llu [multiplier, shift, offset]\\n\", mult, shift, offset);\n}\n\nstatic void dump_option_section(int fd, unsigned int size,\n\t\t\t\tunsigned short id, char *desc, enum dump_items v)\n{\n\tstruct file_section *sec;\n\n\tsec = calloc(1, sizeof(struct file_section));\n\tif (!sec)\n\t\tdie(\"cannot allocate new section\");\n\n\tsec->next = sections;\n\tsections = sec;\n\tsec->id = id;\n\tsec->verbosity = v;\n\tif (read_file_number(fd, &sec->offset, 8))\n\t\tdie(\"cannot read the option %d offset\", id);\n\n\tdo_print(OPTIONS, \"\\t\\t[Option %s, %d bytes] @ %lld\\n\", desc, size, sec->offset);\n}\n\nstatic void dump_sections(int fd, int count)\n{\n\tstruct file_section *sec = sections;\n\tunsigned short flags;\n\n\twhile (sec) {\n\t\tif (lseek(fd, sec->offset, SEEK_SET) == (off_t)-1)\n\t\t\tdie(\"cannot goto option offset %lld\", sec->offset);\n\n\t\tdump_section_header(fd, sec->verbosity, &flags);\n\n\t\tif ((flags & TRACECMD_SEC_FL_COMPRESS) && uncompress_block())\n\t\t\tdie(\"cannot uncompress section block\");\n\n\t\tswitch (sec->id) {\n\t\tcase TRACECMD_OPTION_HEADER_INFO:\n\t\t\tdump_header_page(fd);\n\t\t\tdump_header_event(fd);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_FTRACE_EVENTS:\n\t\t\tdump_ftrace_events_format(fd);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_EVENT_FORMATS:\n\t\t\tdump_events_format(fd);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_KALLSYMS:\n\t\t\tdump_kallsyms(fd);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_PRINTK:\n\t\t\tdump_printk(fd);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_CMDLINES:\n\t\t\tdump_cmdlines(fd);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_MODULES_FILE:\n\t\t\tdump_modules(fd);\n\t\t\tbreak;\n\t\t}\n\t\tuncompress_reset();\n\t\tsec = sec->next;\n\t}\n\tdo_print(SUMMARY|SECTIONS, \"\\t[%d sections]\\n\", count);\n}\n\nstatic int dump_options_read(int fd);\n\nstatic int dump_option_done(int fd, int size)\n{\n\tunsigned long long offset;\n\n\tdo_print(OPTIONS, \"\\t\\t[Option DONE, %d bytes]\\n\", size);\n\n\tif (file_version < FILE_VERSION_SECTIONS || size < 8)\n\t\treturn 0;\n\n\tif (read_file_number(fd, &offset, 8))\n\t\tdie(\"cannot read the next options offset\");\n\n\tdo_print(OPTIONS, \"%lld\\n\", offset);\n\tif (!offset)\n\t\treturn 0;\n\n\tif (lseek(fd, offset, SEEK_SET) == (off_t)-1)\n\t\tdie(\"cannot goto next options offset %lld\", offset);\n\n\tdo_print(OPTIONS, \"\\n\\n\");\n\n\treturn dump_options_read(fd);\n}\n\nstatic int dump_options_read(int fd)\n{\n\tunsigned short flags = 0;\n\tunsigned short option;\n\tunsigned int size;\n\tint count = 0;\n\n\tif (file_version >= FILE_VERSION_SECTIONS)\n\t\tdump_section_header(fd, OPTIONS, &flags);\n\n\tif ((flags & TRACECMD_SEC_FL_COMPRESS) && uncompress_block())\n\t\tdie(\"cannot uncompress file block\");\n\n\tfor (;;) {\n\t\tif (read_file_number(fd, &option, 2))\n\t\t\tdie(\"cannot read the option id\");\n\t\tif (option == TRACECMD_OPTION_DONE && file_version < FILE_VERSION_SECTIONS)\n\t\t\tbreak;\n\t\tif (read_file_number(fd, &size, 4))\n\t\t\tdie(\"cannot read the option size\");\n\n\t\tcount++;\n\t\tswitch (option) {\n\t\tcase TRACECMD_OPTION_DATE:\n\t\t\tdump_option_string(fd, size, \"DATE\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_CPUSTAT:\n\t\t\tdump_option_string(fd, size, \"CPUSTAT\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_BUFFER:\n\t\tcase TRACECMD_OPTION_BUFFER_TEXT:\n\t\t\tdump_option_buffer(fd, option, size);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_TRACECLOCK:\n\t\t\tdo_print(OPTIONS, \"\\t\\t[Option TRACECLOCK, %d bytes]\\n\", size);\n\t\t\tread_dump_string(fd, size, OPTIONS | CLOCK);\n\t\t\thas_clock = 1;\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_UNAME:\n\t\t\tdump_option_string(fd, size, \"UNAME\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_HOOK:\n\t\t\tdump_option_string(fd, size, \"HOOK\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_OFFSET:\n\t\t\tdump_option_string(fd, size, \"OFFSET\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_CPUCOUNT:\n\t\t\tdump_option_int(fd, size, \"CPUCOUNT\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_VERSION:\n\t\t\tdump_option_string(fd, size, \"VERSION\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_PROCMAPS:\n\t\t\tdump_option_string(fd, size, \"PROCMAPS\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_TRACEID:\n\t\t\tdump_option_xlong(fd, size, \"TRACEID\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_TIME_SHIFT:\n\t\t\tdump_option_timeshift(fd, size);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_GUEST:\n\t\t\tdump_option_guest(fd, size);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_TSC2NSEC:\n\t\t\tdump_option_tsc2nsec(fd, size);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_HEADER_INFO:\n\t\t\tdump_option_section(fd, size, option, \"HEADERS\", HEAD_PAGE | HEAD_EVENT);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_FTRACE_EVENTS:\n\t\t\tdump_option_section(fd, size, option, \"FTRACE EVENTS\", FTRACE_FORMAT);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_EVENT_FORMATS:\n\t\t\tdump_option_section(fd, size, option,\n\t\t\t\t\t    \"EVENT FORMATS\", EVENT_SYSTEMS | EVENT_FORMAT);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_KALLSYMS:\n\t\t\tdump_option_section(fd, size, option, \"KALLSYMS\", KALLSYMS);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_PRINTK:\n\t\t\tdump_option_section(fd, size, option, \"PRINTK\", TRACE_PRINTK);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_CMDLINES:\n\t\t\tdump_option_section(fd, size, option, \"CMDLINES\", CMDLINES);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_LAST_BOOT_INFO:\n\t\t\tdump_option_string(fd, size, \"LAST_BOOT_INFO\");\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_MODULES_FILE:\n\t\t\tdump_option_section(fd, size, option, \"MODULES_FILE\", MODULES_FILE);\n\t\t\tbreak;\n\t\tcase TRACECMD_OPTION_DONE:\n\t\t\tuncompress_reset();\n\t\t\tcount += dump_option_done(fd, size);\n\t\t\treturn count;\n\t\tdefault:\n\t\t\tdo_print(OPTIONS, \" %d %d\\t[Unknown option, size - skipping]\\n\",\n\t\t\t\t option, size);\n\t\t\tdo_lseek(fd, size, SEEK_CUR);\n\t\t\tbreak;\n\t\t}\n\t}\n\tuncompress_reset();\n\treturn count;\n}\n\nstatic void dump_options(int fd)\n{\n\tint count;\n\n\tcount = dump_options_read(fd);\n\tdo_print(SUMMARY|OPTIONS, \"\\t[%d options]\\n\", count);\n}\n\nstatic void dump_latency(int fd)\n{\n\tdo_print(SUMMARY, \"\\t[Latency tracing data]\\n\");\n}\n\nstatic void dump_clock(int fd)\n{\n\tlong long size;\n\tchar *clock;\n\n\tdo_print((SUMMARY | CLOCK), \"\\t[Tracing clock]\\n\");\n\tif (!has_clock) {\n\t\tdo_print((SUMMARY | CLOCK), \"\\t\\t No tracing clock saved in the file\\n\");\n\t\treturn;\n\t}\n\tif (read_file_number(fd, &size, 8))\n\t\tdie(\"cannot read clock size\");\n\tclock = calloc(1, size + 1);\n\tif (!clock)\n\t\tdie(\"cannot allocate clock %lld bytes\", size);\n\n\tif (read_file_bytes(fd, clock, size))\n\t\tdie(\"cannot read clock %lld bytes\", size);\n\tclock[size] = 0;\n\tdo_print((SUMMARY | CLOCK), \"\\t\\t%s\\n\", clock);\n\tfree(clock);\n}\n\nstatic void dump_flyrecord(int fd)\n{\n\tlong long cpu_offset;\n\tlong long cpu_size;\n\tint i;\n\n\tdo_print((SUMMARY | FLYRECORD), \"\\t[Flyrecord tracing data]\\n\");\n\n\tfor (i = 0; i < trace_cpus; i++) {\n\t\tif (read_file_number(fd, &cpu_offset, 8))\n\t\t\tdie(\"cannot read the cpu %d offset\", i);\n\t\tif (read_file_number(fd, &cpu_size, 8))\n\t\t\tdie(\"cannot read the cpu %d size\", i);\n\t\tdo_print(FLYRECORD, \"\\t %10.lld %10.lld\\t[offset, size of cpu %d]\\n\",\n\t\t\t cpu_offset, cpu_size, i);\n\t}\n\tdump_clock(fd);\n}\n\nstatic void dump_therest(int fd)\n{\n\tchar str[10];\n\n\tfor (;;) {\n\t\tif (read_file_bytes(fd, str, 10))\n\t\t\tdie(\"cannot read the rest of the header\");\n\n\t\tif (strncmp(str, HEAD_OPTIONS, 10) == 0)\n\t\t\tdump_options(fd);\n\t\telse if (strncmp(str, HEAD_LATENCY, 10) == 0)\n\t\t\tdump_latency(fd);\n\t\telse if (strncmp(str, HEAD_FLYRECORD, 10) == 0)\n\t\t\tdump_flyrecord(fd);\n\t\telse {\n\t\t\tlseek(fd, -10, SEEK_CUR);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic void dump_v6_file(int fd)\n{\n\tdump_header_page(fd);\n\tdump_header_event(fd);\n\tdump_ftrace_events_format(fd);\n\tdump_events_format(fd);\n\tdump_kallsyms(fd);\n\tdump_printk(fd);\n\tdump_cmdlines(fd);\n\tdump_cpus_count(fd);\n\tdump_therest(fd);\n}\n\nstatic int read_metadata_strings(int fd, unsigned long long size)\n{\n\tchar *str, *strings;\n\tint psize;\n\tint ret;\n\n\tstrings = realloc(meta_strings, meta_strings_size + size);\n\tif (!strings)\n\t\treturn -1;\n\tmeta_strings = strings;\n\n\tret = read_file_bytes(fd, meta_strings + meta_strings_size, size);\n\tif (ret < 0)\n\t\treturn -1;\n\n\tdo_print(STRINGS, \"\\t[String @ offset]\\n\");\n\tpsize = 0;\n\twhile (psize < size) {\n\t\tstr = meta_strings + meta_strings_size + psize;\n\t\tdo_print(STRINGS, \"\\t\\t\\\"%s\\\" @ %d\\n\", str, meta_strings_size + psize);\n\t\tpsize += strlen(str) + 1;\n\t}\n\n\tmeta_strings_size += size;\n\n\treturn 0;\n}\n\nstatic void get_meta_strings(int fd)\n{\n\tunsigned long long offset, size;\n\tunsigned int csize, rsize;\n\tunsigned short fl, id;\n\tint desc_id;\n\n\toffset = lseek(fd, 0, SEEK_CUR);\n\tdo {\n\t\tif (read_file_number(fd, &id, 2))\n\t\t\tbreak;\n\t\tif (read_file_number(fd, &fl, 2))\n\t\t\tdie(\"cannot read section flags\");\n\t\tif (read_file_number(fd, &desc_id, 4))\n\t\t\tdie(\"cannot read section description\");\n\t\tif (read_file_number(fd, &size, 8))\n\t\t\tdie(\"cannot read section size\");\n\t\tif (id == TRACECMD_OPTION_STRINGS) {\n\t\t\tif ((fl & TRACECMD_SEC_FL_COMPRESS)) {\n\t\t\t\tread_file_number(fd, &csize, 4);\n\t\t\t\tread_file_number(fd, &rsize, 4);\n\t\t\t\tlseek(fd, -8, SEEK_CUR);\n\t\t\t\tif (uncompress_block())\n\t\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\trsize = size;\n\t\t\t}\n\t\t\tread_metadata_strings(fd, rsize);\n\t\t\tuncompress_reset();\n\t\t} else {\n\t\t\tif (lseek(fd, size, SEEK_CUR) == (off_t)-1)\n\t\t\t\tbreak;\n\t\t}\n\t} while (1);\n\n\tif (lseek(fd, offset, SEEK_SET) == (off_t)-1)\n\t\tdie(\"cannot restore the original file location\");\n}\n\nstatic int walk_v7_sections(int fd)\n{\n\tunsigned long long offset, soffset, size;\n\tunsigned short fl;\n\tunsigned short id;\n\tint csize, rsize;\n\tint count = 0;\n\tint desc_id;\n\tconst char *desc;\n\n\toffset = lseek(fd, 0, SEEK_CUR);\n\tdo {\n\t\tsoffset = lseek(fd, 0, SEEK_CUR);\n\t\tif (read_file_number(fd, &id, 2))\n\t\t\tbreak;\n\n\t\tif (read_file_number(fd, &fl, 2))\n\t\t\tdie(\"cannot read section flags\");\n\n\t\tif (read_file_number(fd, &desc_id, 4))\n\t\t\tdie(\"cannot read section description\");\n\n\t\tdesc = get_metadata_string(desc_id);\n\t\tif (!desc)\n\t\t\tdesc = \"Unknown\";\n\n\t\tif (read_file_number(fd, &size, 8))\n\t\t\tdie(\"cannot read section size\");\n\n\t\tif (id >= TRACECMD_OPTION_MAX)\n\t\t\tdo_print(SECTIONS, \"Unknown section id %d: %s\", id, desc);\n\n\t\tcount++;\n\t\tif (fl & TRACECMD_SEC_FL_COMPRESS) {\n\t\t\tif (id == TRACECMD_OPTION_BUFFER ||\n\t\t\t    id == TRACECMD_OPTION_BUFFER_TEXT) {\n\t\t\t\tdo_print(SECTIONS,\n\t\t\t\t\t\"\\t[Section %2d @ %-16lld\\t\\\"%s\\\", flags 0x%X, \"\n\t\t\t\t\t\"%lld compressed bytes]\\n\",\n\t\t\t\t\t id, soffset, desc, fl, size);\n\t\t\t} else {\n\t\t\t\tif (read_file_number(fd, &csize, 4))\n\t\t\t\t\tdie(\"cannot read section size\");\n\n\t\t\t\tif (read_file_number(fd, &rsize, 4))\n\t\t\t\t\tdie(\"cannot read section size\");\n\n\t\t\t\tdo_print(SECTIONS, \"\\t[Section %2d @ %-16lld\\t\\\"%s\\\", flags 0x%X, \"\n\t\t\t\t\t \"%d compressed, %d uncompressed]\\n\",\n\t\t\t\t\t id, soffset, desc, fl, csize, rsize);\n\t\t\t\tsize -= 8;\n\t\t\t}\n\t\t} else {\n\t\t\tdo_print(SECTIONS, \"\\t[Section %2d @ %-16lld\\t\\\"%s\\\", flags 0x%X, %lld bytes]\\n\",\n\t\t\t\t id, soffset, desc, fl, size);\n\t\t}\n\n\t\tif (lseek(fd, size, SEEK_CUR) == (off_t)-1)\n\t\t\tbreak;\n\t} while (1);\n\n\tif (lseek(fd, offset, SEEK_SET) == (off_t)-1)\n\t\tdie(\"cannot restore the original file location\");\n\n\treturn count;\n}\n\nstatic void dump_v7_file(int fd)\n{\n\tlong long offset;\n\tint sections;\n\n\tif (read_file_number(fd, &offset, 8))\n\t\tdie(\"cannot read offset of the first option section\");\n\n\tget_meta_strings(fd);\n\tsections = walk_v7_sections(fd);\n\n\tif (lseek(fd, offset, SEEK_SET) == (off_t)-1)\n\t\tdie(\"cannot goto options offset %lld\", offset);\n\n\tdump_options(fd);\n\tdump_sections(fd, sections);\n}\n\nstatic void free_sections(void)\n{\n\tstruct file_section *del;\n\n\twhile (sections) {\n\t\tdel = sections;\n\t\tsections = sections->next;\n\t\tfree(del);\n\t}\n}\n\nstatic void dump_file(const char *file)\n{\n\tint fd;\n\n\ttep = tep_alloc();\n\tif (!tep)\n\t\treturn;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0)\n\t\tdie(\"cannot open '%s'\\n\", file);\n\n\tdo_print(SUMMARY, \"\\n Tracing meta data in file %s:\\n\", file);\n\n\tdump_initial_format(fd);\n\tdump_compress(fd);\n\tif (file_version < FILE_VERSION_SECTIONS)\n\t\tdump_v6_file(fd);\n\telse\n\t\tdump_v7_file(fd);\n\tfree_sections();\n\ttep_free(tep);\n\ttep = NULL;\n\tclose(fd);\n}\n\nenum {\n\tOPT_sections\t= 240,\n\tOPT_strings\t= 241,\n\tOPT_verbose\t= 242,\n\tOPT_clock\t= 243,\n\tOPT_all\t\t= 244,\n\tOPT_summary\t= 245,\n\tOPT_flyrecord\t= 246,\n\tOPT_options\t= 247,\n\tOPT_cmd_lines\t= 248,\n\tOPT_printk\t= 249,\n\tOPT_kallsyms\t= 250,\n\tOPT_events\t= 251,\n\tOPT_systems\t= 252,\n\tOPT_ftrace\t= 253,\n\tOPT_head_event\t= 254,\n\tOPT_head_page\t= 255,\n\tOPT_modules\t= 256,\n};\n\nvoid trace_dump(int argc, char **argv)\n{\n\tchar *input_file = NULL;\n\tbool validate = false;\n\tint c;\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"dump\") != 0)\n\t\tusage(argv);\n\tfor (;;) {\n\t\tint option_index = 0;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"all\", no_argument, NULL, OPT_all},\n\t\t\t{\"summary\", no_argument, NULL, OPT_summary},\n\t\t\t{\"head-page\", no_argument, NULL, OPT_head_page},\n\t\t\t{\"head-event\", no_argument, NULL, OPT_head_event},\n\t\t\t{\"ftrace-events\", no_argument, NULL, OPT_ftrace},\n\t\t\t{\"systems\", no_argument, NULL, OPT_systems},\n\t\t\t{\"events\", no_argument, NULL, OPT_events},\n\t\t\t{\"kallsyms\", no_argument, NULL, OPT_kallsyms},\n\t\t\t{\"printk\", no_argument, NULL, OPT_printk},\n\t\t\t{\"cmd-lines\", no_argument, NULL, OPT_cmd_lines},\n\t\t\t{\"modules\", no_argument, NULL, OPT_modules},\n\t\t\t{\"options\", no_argument, NULL, OPT_options},\n\t\t\t{\"flyrecord\", no_argument, NULL, OPT_flyrecord},\n\t\t\t{\"clock\", no_argument, NULL, OPT_clock},\n\t\t\t{\"strings\", no_argument, NULL, OPT_strings},\n\t\t\t{\"sections\", no_argument, NULL, OPT_sections},\n\t\t\t{\"validate\", no_argument, NULL, 'v'},\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{\"verbose\", optional_argument, NULL, OPT_verbose},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long (argc-1, argv+1, \"+hvai:\",\n\t\t\t\tlong_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\tinput_file = optarg;\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\tvalidate = true;\n\t\t\tbreak;\n\t\tcase OPT_all:\n\t\t\tverbosity = 0xFFFFFFFF;\n\t\t\tbreak;\n\t\tcase OPT_summary:\n\t\t\tverbosity |= SUMMARY;\n\t\t\tbreak;\n\t\tcase OPT_flyrecord:\n\t\t\tverbosity |= FLYRECORD;\n\t\t\tbreak;\n\t\tcase OPT_options:\n\t\t\tverbosity |= OPTIONS;\n\t\t\tbreak;\n\t\tcase OPT_cmd_lines:\n\t\t\tverbosity |= CMDLINES;\n\t\t\tbreak;\n\t\tcase OPT_printk:\n\t\t\tverbosity |= TRACE_PRINTK;\n\t\t\tbreak;\n\t\tcase OPT_kallsyms:\n\t\t\tverbosity |= KALLSYMS;\n\t\t\tbreak;\n\t\tcase OPT_events:\n\t\t\tverbosity |= EVENT_FORMAT;\n\t\t\tbreak;\n\t\tcase OPT_systems:\n\t\t\tverbosity |= EVENT_SYSTEMS;\n\t\t\tbreak;\n\t\tcase OPT_ftrace:\n\t\t\tverbosity |= FTRACE_FORMAT;\n\t\t\tbreak;\n\t\tcase OPT_head_event:\n\t\t\tverbosity |= HEAD_EVENT;\n\t\t\tbreak;\n\t\tcase OPT_head_page:\n\t\t\tverbosity |= HEAD_PAGE;\n\t\t\tbreak;\n\t\tcase OPT_clock:\n\t\t\tverbosity |= CLOCK;\n\t\t\tbreak;\n\t\tcase OPT_modules:\n\t\t\tverbosity |= MODULES_FILE;\n\t\t\tbreak;\n\t\tcase OPT_verbose:\n\t\t\tif (trace_set_verbose(optarg) < 0)\n\t\t\t\tdie(\"invalid verbose level %s\", optarg);\n\t\t\tbreak;\n\t\tcase OPT_strings:\n\t\t\tverbosity |= STRINGS;\n\t\t\tbreak;\n\t\tcase OPT_sections:\n\t\t\tverbosity |= SECTIONS;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif ((argc - optind) >= 2) {\n\t\tif (input_file)\n\t\t\tusage(argv);\n\t\tinput_file = argv[optind + 1];\n\t}\n\n\tif (!input_file)\n\t\tinput_file = DEFAULT_INPUT_FILE;\n\n\tif (!verbosity && !validate)\n\t\tverbosity = SUMMARY;\n\n\tdump_file(input_file);\n\n\tif (validate)\n\t\ttracecmd_plog(\"File %s is a valid trace-cmd file\\n\", input_file);\n}\n"
  },
  {
    "path": "tracecmd/trace-hist.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2013 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n * Several of the ideas in this file came from Arnaldo Carvalho de Melo's\n * work on the perf ui.\n */\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <signal.h>\n\n#include \"trace-hash-local.h\"\n#include \"trace-local.h\"\n#include \"list.h\"\n\nstatic int sched_wakeup_type;\nstatic int sched_wakeup_new_type;\nstatic int sched_switch_type;\nstatic int function_type;\nstatic int function_graph_entry_type;\nstatic int function_graph_exit_type;\nstatic int kernel_stack_type;\n\nstatic int long_size;\n\nstatic struct tep_format_field *common_type_hist;\nstatic struct tep_format_field *common_pid_field;\nstatic struct tep_format_field *sched_wakeup_comm_field;\nstatic struct tep_format_field *sched_wakeup_new_comm_field;\nstatic struct tep_format_field *sched_wakeup_pid_field;\nstatic struct tep_format_field *sched_wakeup_new_pid_field;\nstatic struct tep_format_field *sched_switch_prev_field;\nstatic struct tep_format_field *sched_switch_next_field;\nstatic struct tep_format_field *sched_switch_prev_pid_field;\nstatic struct tep_format_field *sched_switch_next_pid_field;\nstatic struct tep_format_field *function_ip_field;\nstatic struct tep_format_field *function_parent_ip_field;\nstatic struct tep_format_field *function_graph_entry_func_field;\nstatic struct tep_format_field *function_graph_entry_depth_field;\nstatic struct tep_format_field *function_graph_exit_func_field;\nstatic struct tep_format_field *function_graph_exit_depth_field;\nstatic struct tep_format_field *function_graph_exit_calltime_field;\nstatic struct tep_format_field *function_graph_exit_rettime_field;\nstatic struct tep_format_field *function_graph_exit_overrun_field;\nstatic struct tep_format_field *kernel_stack_caller_field;\n\nstatic int compact;\n\nstatic void *zalloc(size_t size)\n{\n\treturn calloc(1, size);\n}\n\nstatic const char **ips;\nstatic int ips_idx;\nstatic int func_depth;\nstatic int current_pid = -1;\n\nstruct stack_save {\n\tstruct stack_save\t*next;\n\tconst char\t\t**ips;\n\tint\t\t\tips_idx;\n\tint\t\t\tfunc_depth;\n\tint\t\t\tpid;\n};\n\nstruct stack_save *saved_stacks;\n\nstatic void reset_stack(void)\n{\n\tcurrent_pid = -1;\n\tips_idx = 0;\n\tfunc_depth = 0;\n\t/* Don't free here, it may be saved */\n\tips = NULL;\n}\n\nstatic void save_stack(void)\n{\n\tstruct stack_save *stack;\n\n\tstack = zalloc(sizeof(*stack));\n\tif (!stack)\n\t\tdie(\"malloc\");\n\n\tstack->pid = current_pid;\n\tstack->ips_idx = ips_idx;\n\tstack->func_depth = func_depth;\n\tstack->ips = ips;\n\n\tstack->next = saved_stacks;\n\tsaved_stacks = stack;\n\n\treset_stack();\n}\n\nstatic void restore_stack(int pid)\n{\n\tstruct stack_save *last = NULL, *stack;\n\n\tfor (stack = saved_stacks; stack; last = stack, stack = stack->next) {\n\t\tif (stack->pid == pid)\n\t\t\tbreak;\n\t}\n\n\tif (!stack)\n\t\treturn;\n\n\tif (last)\n\t\tlast->next = stack->next;\n\telse\n\t\tsaved_stacks = stack->next;\n\n\tcurrent_pid = stack->pid;\n\tips_idx = stack->ips_idx;\n\tfunc_depth = stack->func_depth;\n\tfree(ips);\n\tips = stack->ips;\n\tfree(stack);\n}\n\nstruct pid_list;\n\nstruct chain {\n\tstruct chain\t\t*next;\n\tstruct chain\t\t*sibling;\n\tconst char\t\t*func;\n\tstruct chain\t\t*parents;\n\tstruct pid_list\t\t*pid_list;\n\tint\t\t\tnr_parents;\n\tint\t\t\tcount;\n\tint\t\t\ttotal;\n\tint\t\t\tevent;\n};\nstatic struct chain *chains;\nstatic int nr_chains;\nstatic int total_counts;\n\nstruct pid_list {\n\tstruct pid_list\t\t*next;\n\tstruct chain\t\tchain;\n\tint\t\t\tpid;\n};\nstatic struct pid_list *list_pids;\nstatic struct pid_list all_pid_list;\n\nstatic void add_chain(struct chain *chain)\n{\n\tif (chain->next)\n\t\tdie(\"chain not null?\");\n\tchain->next = chains;\n\tchains = chain;\n\tnr_chains++;\n}\n\nstatic void\ninsert_chain(struct pid_list *pid_list, struct chain *chain_list,\n\t     const char **chain_str, int size, int event)\n{\n\tstruct chain *chain;\n\n\t/* Record all counts */\n\tif (!chain_list->func)\n\t\ttotal_counts++;\n\n\tchain_list->count++;\n\n\tif (!size--)\n\t\treturn;\n\n\tfor (chain = chain_list->parents; chain; chain = chain->sibling) {\n\t\tif (chain->func == chain_str[size]) {\n\t\t\tinsert_chain(pid_list, chain, chain_str, size, 0);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tchain_list->nr_parents++;\n\tchain = zalloc(sizeof(struct chain));\n\tif (!chain)\n\t\tdie(\"malloc\");\n\tchain->sibling = chain_list->parents;\n\tchain_list->parents = chain;\n\tchain->func = chain_str[size];\n\tchain->pid_list = pid_list;\n\tchain->event = event;\n\n\t/* NULL func means this is the top level of the chain. Store it */\n\tif (!chain_list->func)\n\t\tadd_chain(chain);\n\n\tinsert_chain(pid_list, chain, chain_str, size, 0);\n}\n\nstatic void save_call_chain(int pid, const char **chain, int size, int event)\n{\n\tstatic struct pid_list *pid_list;\n\n\tif (compact)\n\t\tpid_list = &all_pid_list;\n\n\telse if (!pid_list || pid_list->pid != pid) {\n\t\tfor (pid_list = list_pids; pid_list; pid_list = pid_list->next) {\n\t\t\tif (pid_list->pid == pid)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!pid_list) {\n\t\t\tpid_list = zalloc(sizeof(*pid_list));\n\t\t\tif (!pid_list)\n\t\t\t\tdie(\"malloc\");\n\t\t\tpid_list->pid = pid;\n\t\t\tpid_list->next = list_pids;\n\t\t\tlist_pids = pid_list;\n\t\t}\n\t}\n\tinsert_chain(pid_list, &pid_list->chain, chain, size, event);\n}\n\nstatic void save_stored_stacks(void)\n{\n\twhile (saved_stacks) {\n\t\trestore_stack(saved_stacks->pid);\n\t\tsave_call_chain(current_pid, ips, ips_idx, 0);\n\t}\n}\n\nstatic void flush_stack(void)\n{\n\tif (current_pid < 0)\n\t\treturn;\n\n\tsave_call_chain(current_pid, ips, ips_idx, 0);\n\tfree(ips);\n\treset_stack();\n}\n\nstatic void push_stack_func(const char *func)\n{\n\tips_idx++;\n\tips = realloc(ips, ips_idx * sizeof(char *));\n\tips[ips_idx - 1] = func;\n}\n\nstatic void pop_stack_func(void)\n{\n\tips_idx--;\n\tips[ips_idx] = NULL;\n}\n\nstatic void\nprocess_function(struct tep_handle *pevent, struct tep_record *record)\n{\n\tunsigned long long parent_ip;\n\tunsigned long long ip;\n\tunsigned long long val;\n\tconst char *parent;\n\tconst char *func;\n\tint pid;\n\tint ret;\n\n\tret = tep_read_number_field(common_pid_field, record->data, &val);\n\tif (ret < 0)\n\t\tdie(\"no pid field for function?\");\n\n\tret = tep_read_number_field(function_ip_field, record->data, &ip);\n\tif (ret < 0)\n\t\tdie(\"no ip field for function?\");\n\n\tret = tep_read_number_field(function_parent_ip_field, record->data, &parent_ip);\n\tif (ret < 0)\n\t\tdie(\"no parent ip field for function?\");\n\n\tpid = val;\n\n\tfunc = tep_find_function(pevent, ip);\n\tparent = tep_find_function(pevent, parent_ip);\n\n\tif (current_pid >= 0 && pid != current_pid) {\n\t\tsave_stack();\n\t\trestore_stack(pid);\n\t}\n\n\tcurrent_pid = pid;\n\n\tif (ips_idx) {\n\t\tif (ips[ips_idx - 1] == parent)\n\t\t\tpush_stack_func(func);\n\t\telse {\n\t\t\tsave_call_chain(pid, ips, ips_idx, 0);\n\t\t\twhile (ips_idx) {\n\t\t\t\tpop_stack_func();\n\t\t\t\tif (ips[ips_idx - 1] == parent) {\n\t\t\t\t\tpush_stack_func(func);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* The above check can set ips_idx to zero again */\n\tif (!ips_idx) {\n\t\tpush_stack_func(parent);\n\t\tpush_stack_func(func);\n\t}\n}\n\nstatic void\nprocess_function_graph_entry(struct tep_handle *pevent, struct tep_record *record)\n{\n\tunsigned long long depth;\n\tunsigned long long ip;\n\tunsigned long long val;\n\tconst char *func;\n\tint pid;\n\tint ret;\n\n\tret = tep_read_number_field(common_pid_field, record->data, &val);\n\tif (ret < 0)\n\t\tdie(\"no pid field for function graph entry?\");\n\n\tret = tep_read_number_field(function_graph_entry_func_field,\n\t\t\t\t    record->data, &ip);\n\tif (ret < 0)\n\t\tdie(\"no ip field for function graph entry?\");\n\n\tret = tep_read_number_field(function_graph_entry_depth_field,\n\t\t\t\t    record->data, &depth);\n\tif (ret < 0)\n\t\tdie(\"no parent ip field for function entry?\");\n\n\tpid = val;\n\n\tfunc = tep_find_function(pevent, ip);\n\n\tif (current_pid >= 0 && pid != current_pid) {\n\t\tsave_stack();\n\t\trestore_stack(pid);\n\t}\n\n\tcurrent_pid = pid;\n\n\tif (depth != ips_idx) {\n\t\tsave_call_chain(pid, ips, ips_idx, 0);\n\t\twhile (ips_idx > depth)\n\t\t\tpop_stack_func();\n\t}\n\n\tfunc_depth = depth;\n\n\tpush_stack_func(func);\n}\n\nstatic void\nprocess_function_graph_exit(struct tep_handle *pevent, struct tep_record *record)\n{\n\tunsigned long long depth;\n\tunsigned long long val;\n\tint pid;\n\tint ret;\n\n\tret = tep_read_number_field(common_pid_field, record->data, &val);\n\tif (ret < 0)\n\t\tdie(\"no pid field for function graph exit?\");\n\n\tret = tep_read_number_field(function_graph_exit_depth_field,\n\t\t\t\t    record->data, &depth);\n\tif (ret < 0)\n\t\tdie(\"no parent ip field for function?\");\n\n\tpid = val;\n\n\tif (current_pid >= 0 && pid != current_pid) {\n\t\tsave_stack();\n\t\trestore_stack(pid);\n\t}\n\n\tcurrent_pid = pid;\n\n\tif (ips_idx != depth) {\n\t\tsave_call_chain(pid, ips, ips_idx, 0);\n\t\twhile (ips_idx > depth)\n\t\t\tpop_stack_func();\n\t}\n\n\tfunc_depth = depth - 1;\n}\n\nstatic int pending_pid = -1;\nstatic const char **pending_ips;\nstatic int pending_ips_idx;\n\nstatic void reset_pending_stack(void)\n{\n\tpending_pid = -1;\n\tpending_ips_idx = 0;\n\tfree(pending_ips);\n\tpending_ips = NULL;\n}\n\nstatic void copy_stack_to_pending(int pid)\n{\n\tpending_pid = pid;\n\tpending_ips = zalloc(sizeof(char *) * ips_idx);\n\tmemcpy(pending_ips, ips, sizeof(char *) * ips_idx);\n\tpending_ips_idx = ips_idx;\n}\n\nstatic void\nprocess_kernel_stack(struct tep_handle *pevent, struct tep_record *record)\n{\n\tstruct tep_format_field *field = kernel_stack_caller_field;\n\tunsigned long long val;\n\tvoid *data = record->data;\n\tint do_restore = 0;\n\tint pid;\n\tint ret;\n\n\tret = tep_read_number_field(common_pid_field, record->data, &val);\n\tif (ret < 0)\n\t\tdie(\"no pid field for function?\");\n\tpid = val;\n\n\tif (pending_pid >= 0 && pid != pending_pid) {\n\t\treset_pending_stack();\n\t\treturn;\n\t}\n\n\tif (!field)\n\t\tdie(\"no caller field for kernel stack?\");\n\n\tif (pending_pid >= 0) {\n\t\tif (current_pid >= 0) {\n\t\t\tsave_stack();\n\t\t\tdo_restore = 1;\n\t\t}\n\t} else {\n\t\t/* function stack trace? */\n\t\tif (current_pid >= 0) {\n\t\t\tcopy_stack_to_pending(current_pid);\n\t\t\tfree(ips);\n\t\t\treset_stack();\n\t\t}\n\t}\n\n\tcurrent_pid = pid;\n\n\t/* Need to start at the end of the callers and work up */\n\tfor (data += field->offset; data < record->data + record->size;\n\t     data += long_size) {\n\t\tunsigned long long addr;\n\n\t\taddr = tep_read_number(pevent, data, long_size);\n\n\t\tif ((long_size == 8 && addr == (unsigned long long)-1) ||\n\t\t    ((int)addr == -1))\n\t\t\tbreak;\n\t}\n\n\tfor (data -= long_size; data >= record->data + field->offset; data -= long_size) {\n\t\tunsigned long long addr;\n\t\tconst char *func;\n\n\t\taddr = tep_read_number(pevent, data, long_size);\n\t\tfunc = tep_find_function(pevent, addr);\n\t\tif (func)\n\t\t\tpush_stack_func(func);\n\t}\n\n\tif (pending_pid >= 0) {\n\t\tpush_stack_func(pending_ips[pending_ips_idx - 1]);\n\t\treset_pending_stack();\n\t}\n\tsave_call_chain(current_pid, ips, ips_idx, 1);\n\tif (do_restore)\n\t\trestore_stack(current_pid);\n}\n\nstatic void\nprocess_sched_wakeup(struct tep_handle *pevent, struct tep_record *record, int type)\n{\n\tunsigned long long val;\n\tconst char *comm;\n\tint pid;\n\tint ret;\n\n\tif (type == sched_wakeup_type) {\n\t\tcomm = (char *)(record->data + sched_wakeup_comm_field->offset);\n\t\tret = tep_read_number_field(sched_wakeup_pid_field, record->data, &val);\n\t\tif (ret < 0)\n\t\t\tdie(\"no pid field in sched_wakeup?\");\n\t} else {\n\t\tcomm = (char *)(record->data + sched_wakeup_new_comm_field->offset);\n\t\tret = tep_read_number_field(sched_wakeup_new_pid_field, record->data, &val);\n\t\tif (ret < 0)\n\t\t\tdie(\"no pid field in sched_wakeup_new?\");\n\t}\n\n\tpid = val;\n\n\ttep_register_comm(pevent, comm, pid);\n}\n\nstatic void\nprocess_sched_switch(struct tep_handle *pevent, struct tep_record *record)\n{\n\tunsigned long long val;\n\tconst char *comm;\n\tint pid;\n\tint ret;\n\n\tcomm = (char *)(record->data + sched_switch_prev_field->offset);\n\tret = tep_read_number_field(sched_switch_prev_pid_field, record->data, &val);\n\tif (ret < 0)\n\t\tdie(\"no prev_pid field in sched_switch?\");\n\tpid = val;\n\ttep_register_comm(pevent, comm, pid);\n\n\tcomm = (char *)(record->data + sched_switch_next_field->offset);\n\tret = tep_read_number_field(sched_switch_next_pid_field, record->data, &val);\n\tif (ret < 0)\n\t\tdie(\"no next_pid field in sched_switch?\");\n\tpid = val;\n\ttep_register_comm(pevent, comm, pid);\n}\n\nstatic void\nprocess_event(struct tep_handle *pevent, struct tep_record *record, int type)\n{\n\tstruct tep_event *event;\n\tconst char *event_name;\n\tunsigned long long val;\n\tint pid;\n\tint ret;\n\n\tif (pending_pid >= 0) {\n\t\tsave_call_chain(pending_pid, pending_ips, pending_ips_idx, 1);\n\t\treset_pending_stack();\n\t}\n\t\t\n\tevent = tep_find_event(pevent, type);\n\tevent_name = event->name;\n\n\tret = tep_read_number_field(common_pid_field, record->data, &val);\n\tif (ret < 0)\n\t\tdie(\"no pid field for function?\");\n\n\tpid = val;\n\n\t/*\n\t * Even if function or function graph tracer is running,\n\t * if the user ran with stack traces on events, we want to use\n\t * that instead. But unfortunately, that stack doesn't come\n\t * until after the event. Thus, we only add the event into\n\t * the pending stack.\n\t */\n\tpush_stack_func(event_name);\n\tcopy_stack_to_pending(pid);\n\tpop_stack_func();\n}\n\nstatic void\nprocess_record(struct tep_handle *pevent, struct tep_record *record)\n{\n\tunsigned long long val;\n\tint type;\n\n\ttep_read_number_field(common_type_hist, record->data, &val);\n\ttype = val;\n\n\tif (type == function_type)\n\t\treturn process_function(pevent, record);\n\n\tif (type == function_graph_entry_type)\n\t\treturn process_function_graph_entry(pevent, record);\n\n\tif (type == function_graph_exit_type)\n\t\treturn process_function_graph_exit(pevent, record);\n\n\tif (type == kernel_stack_type)\n\t\treturn process_kernel_stack(pevent, record);\n\n\tif (type == sched_wakeup_type || type == sched_wakeup_new_type)\n\t\tprocess_sched_wakeup(pevent, record, type);\n\n\telse if (type == sched_switch_type)\n\t\tprocess_sched_switch(pevent, record);\n\n\tprocess_event(pevent, record, type);\n}\n\nstatic struct tep_event *\nupdate_event(struct tep_handle *pevent,\n\t     const char *sys, const char *name, int *id)\n{\n\tstruct tep_event *event;\n\n\tevent = tep_find_event_by_name(pevent, sys, name);\n\tif (!event)\n\t\treturn NULL;\n\n\t*id = event->id;\n\n\treturn event;\n}\n\nstatic void update_sched_wakeup(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"sched\", \"sched_wakeup\", &sched_wakeup_type);\n\tif (!event)\n\t\treturn;\n\n\tsched_wakeup_comm_field = tep_find_field(event, \"comm\");\n\tsched_wakeup_pid_field = tep_find_field(event, \"pid\");\n}\n\nstatic void update_sched_wakeup_new(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"sched\", \"sched_wakeup_new\", &sched_wakeup_new_type);\n\tif (!event)\n\t\treturn;\n\n\tsched_wakeup_new_comm_field = tep_find_field(event, \"comm\");\n\tsched_wakeup_new_pid_field = tep_find_field(event, \"pid\");\n}\n\nstatic void update_sched_switch(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"sched\", \"sched_switch\", &sched_switch_type);\n\tif (!event)\n\t\treturn;\n\n\tsched_switch_prev_field = tep_find_field(event, \"prev_comm\");\n\tsched_switch_next_field = tep_find_field(event, \"next_comm\");\n\tsched_switch_prev_pid_field = tep_find_field(event, \"prev_pid\");\n\tsched_switch_next_pid_field = tep_find_field(event, \"next_pid\");\n}\n\nstatic void update_function(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"ftrace\", \"function\", &function_type);\n\tif (!event)\n\t\treturn;\n\n\tfunction_ip_field = tep_find_field(event, \"ip\");\n\tfunction_parent_ip_field = tep_find_field(event, \"parent_ip\");\n}\n\nstatic void update_function_graph_entry(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"ftrace\", \"funcgraph_entry\", &function_graph_entry_type);\n\tif (!event)\n\t\treturn;\n\n\tfunction_graph_entry_func_field = tep_find_field(event, \"func\");\n\tfunction_graph_entry_depth_field = tep_find_field(event, \"depth\");\n}\n\nstatic void update_function_graph_exit(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"ftrace\", \"funcgraph_exit\", &function_graph_exit_type);\n\tif (!event)\n\t\treturn;\n\n\tfunction_graph_exit_func_field = tep_find_field(event, \"func\");\n\tfunction_graph_exit_depth_field = tep_find_field(event, \"depth\");\n\tfunction_graph_exit_calltime_field = tep_find_field(event, \"calltime\");\n\tfunction_graph_exit_rettime_field = tep_find_field(event, \"rettime\");\n\tfunction_graph_exit_overrun_field = tep_find_field(event, \"overrun\");\n}\n\nstatic void update_kernel_stack(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"ftrace\", \"kernel_stack\", &kernel_stack_type);\n\tif (!event)\n\t\treturn;\n\n\tkernel_stack_caller_field = tep_find_field(event, \"caller\");\n}\n\nenum field { NEXT_PTR, SIB_PTR };\n\nstatic struct chain *next_ptr(struct chain *chain, enum field field)\n{\n\tif (field == NEXT_PTR)\n\t\treturn chain->next;\n\treturn chain->sibling;\n}\n\nstatic struct chain *split_chain(struct chain *orig, int size, enum field field)\n{\n\tstruct chain *chain;\n\tint i;\n\n\tif (size < 2)\n\t\treturn NULL;\n\n\tfor (i = 1; i < (size + 1) / 2; i++, orig = next_ptr(orig, field))\n\t\t;\n\n\tif (field == NEXT_PTR) {\n\t\tchain = orig->next;\n\t\torig->next = NULL;\n\t} else {\n\t\tchain = orig->sibling;\n\t\torig->sibling = NULL;\n\t}\n\n\treturn chain;\n}\n\nstatic struct chain *\nmerge_chains(struct chain *a, int nr_a, struct chain *b, int nr_b, enum field field)\n{\n\tstruct chain *chain;\n\tstruct chain *final;\n\tstruct chain **next = &final;\n\tint i;\n\n\tif (!a)\n\t\treturn b;\n\tif (!b)\n\t\treturn a;\n\n\tfor (i = 0, chain = a; chain; i++, chain = next_ptr(chain, field))\n\t\t;\n\tif (i != nr_a)\n\t\tdie(\"WTF %d %d\", i, nr_a);\n\n\tchain = split_chain(a, nr_a, field);\n\ta = merge_chains(chain, nr_a / 2, a, (nr_a + 1) / 2, field);\n\n\tchain = split_chain(b, nr_b, field);\n\tb = merge_chains(chain, nr_b / 2, b, (nr_b + 1) / 2, field);\n\n\twhile (a && b) {\n\t\tif (a->count > b->count) {\n\t\t\t*next = a;\n\t\t\tif (field == NEXT_PTR)\n\t\t\t\tnext = &a->next;\n\t\t\telse\n\t\t\t\tnext = &a->sibling;\n\t\t\ta = *next;\n\t\t\t*next = NULL;\n\t\t} else {\n\t\t\t*next = b;\n\t\t\tif (field == NEXT_PTR)\n\t\t\t\tnext = &b->next;\n\t\t\telse\n\t\t\t\tnext = &b->sibling;\n\t\t\tb = *next;\n\t\t\t*next = NULL;\n\t\t}\n\t}\n\tif (a)\n\t\t*next = a;\n\telse\n\t\t*next = b;\n\n\treturn final;\n}\n\nstatic void sort_chain_parents(struct chain *chain)\n{\n\tstruct chain *parent;\n\n\tparent = split_chain(chain->parents, chain->nr_parents, SIB_PTR);\n\tchain->parents = merge_chains(parent, chain->nr_parents / 2,\n\t\t\t\t      chain->parents, (chain->nr_parents + 1) / 2,\n\t\t\t\t      SIB_PTR);\n\n\tfor (chain = chain->parents; chain; chain = chain->sibling)\n\t\tsort_chain_parents(chain);\n}\n\nstatic void sort_chains(void)\n{\n\tstruct chain *chain;\n\n\tchain = split_chain(chains, nr_chains, NEXT_PTR);\n\n\t/* The original always has more or equal to the split */\n\tchains = merge_chains(chain, nr_chains / 2, chains, (nr_chains + 1) / 2, NEXT_PTR);\n\n\tfor (chain = chains; chain; chain = chain->next)\n\t\tsort_chain_parents(chain);\n}\n\nstatic double get_percent(int total, int partial)\n{\n\treturn ((double)partial / (double)total) * 100.0;\n}\n\nstatic int single_chain(struct chain *chain)\n{\n\tif (chain->nr_parents > 1)\n\t\treturn 0;\n\n\tif (!chain->parents)\n\t\treturn 1;\n\n\treturn single_chain(chain->parents);\n}\n\n#define START\t\"         |\\n\"\n#define TICK\t\"         --- \"\n#define BLANK\t\"          \"\n#define LINE\t\"            |\"\n#define INDENT\t\"             \"\n\nunsigned long long line_mask;\nvoid make_indent(int indent)\n{\n\tint i;\n\n\tfor (i = 0; i < indent; i++) {\n\t\tif (line_mask & (1 << i))\n\t\t\tprintf(LINE);\n\t\telse\n\t\t\tprintf(INDENT);\n\t}\n}\n\nstatic void\nprint_single_parent(struct chain *chain, int indent)\n{\n\tmake_indent(indent);\n\n\tprintf(BLANK);\n\tprintf(\"%s\\n\", chain->parents->func);\n}\n\nstatic void\ndump_chain(struct tep_handle *pevent, struct chain *chain, int indent)\n{\n\tif (!chain->parents)\n\t\treturn;\n\n\tprint_single_parent(chain, indent);\n\tdump_chain(pevent, chain->parents, indent);\n}\n\nstatic void print_parents(struct tep_handle *pevent, struct chain *chain, int indent)\n{\n\tstruct chain *parent = chain->parents;\n\tint x;\n\n\tif (single_chain(chain)) {\n\t\tdump_chain(pevent, chain, indent);\n\t\treturn;\n\t}\n\n\tline_mask |= 1ULL << (indent);\n\n\tfor (x = 0; parent; x++, parent = parent->sibling) {\n\t\tstruct chain *save_parent;\n\n\t\tmake_indent(indent + 1);\n\t\tprintf(\"\\n\");\n\n\t\tmake_indent(indent + 1);\n\n\t\tprintf(\"--%%%.2f-- %s  # %d\\n\",\n\t\t       get_percent(chain->count, parent->count),\n\t\t       parent->func, parent->count);\n\n\t\tif (x == chain->nr_parents - 1)\n\t\t\tline_mask &= (1ULL << indent) - 1;\n\n\t\tif (single_chain(parent))\n\t\t\tdump_chain(pevent, parent, indent + 1);\n\t\telse {\n\t\t\tsave_parent = parent;\n\n\t\t\twhile (parent && parent->parents && parent->nr_parents < 2 &&\n\t\t\t       parent->parents->count == parent->count) {\n\t\t\t\tprint_single_parent(parent, indent + 1);\n\t\t\t\tparent = parent->parents;\n\t\t\t}\n\t\t\tif (parent)\n\t\t\t\tprint_parents(pevent, parent, indent + 1);\n\t\t\tparent = save_parent;\n\t\t}\n\t}\n}\n\nstatic void print_chains(struct tep_handle *pevent)\n{\n\tstruct chain *chain = chains;\n\tint pid;\n\n\tfor (; chain; chain = chain->next) {\n\t\tpid = chain->pid_list->pid;\n\t\tif (chain != chains)\n\t\t\tprintf(\"\\n\");\n\t\tif (compact)\n\t\t\tprintf(\"  %%%3.2f <all pids> %30s #%d\\n\",\n\t\t\t       get_percent(total_counts, chain->count),\n\t\t\t       chain->func,\n\t\t\t       chain->count);\n\t\telse\n\t\t\tprintf(\"  %%%3.2f  (%d) %s %30s #%d\\n\",\n\t\t\t       get_percent(total_counts, chain->count),\n\t\t\t       pid,\n\t\t\t       tep_data_comm_from_pid(pevent, pid),\n\t\t\t       chain->func,\n\t\t\t       chain->count);\n\t\tprintf(START);\n\t\tif (chain->event)\n\t\t\tprintf(TICK \"*%s*\\n\", chain->func);\n\t\telse\n\t\t\tprintf(TICK \"%s\\n\", chain->func);\n\t\tprint_parents(pevent, chain, 0);\n\t}\n}\n\nstatic void do_trace_hist(struct tracecmd_input *handle)\n{\n\tstruct tep_handle *pevent = tracecmd_get_tep(handle);\n\tstruct tep_record *record;\n\tstruct tep_event *event;\n\tint cpus;\n\tint cpu;\n\tint ret;\n\n\tcpus = tracecmd_cpus(handle);\n\n\t/* Need to get any event */\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\trecord = tracecmd_peek_data(handle, cpu);\n\t\tif (record)\n\t\t\tbreak;\n\t}\n\tif (!record)\n\t\tdie(\"No records found in file\");\n\n\tret = tep_data_type(pevent, record);\n\tevent = tep_find_event(pevent, ret);\n\n\tlong_size = tracecmd_long_size(handle);\n\n\tcommon_type_hist = tep_find_common_field(event, \"common_type\");\n\tif (!common_type_hist)\n\t\tdie(\"Can't find a 'type' field?\");\n\n\tcommon_pid_field = tep_find_common_field(event, \"common_pid\");\n\tif (!common_pid_field)\n\t\tdie(\"Can't find a 'pid' field?\");\n\n\tupdate_sched_wakeup(pevent);\n\tupdate_sched_wakeup_new(pevent);\n\tupdate_sched_switch(pevent);\n\tupdate_function(pevent);\n\tupdate_function_graph_entry(pevent);\n\tupdate_function_graph_exit(pevent);\n\tupdate_kernel_stack(pevent);\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tfor (;;) {\n\t\t\tstruct tep_record *record;\n\n\t\t\trecord = tracecmd_read_data(handle, cpu);\n\t\t\tif (!record)\n\t\t\t\tbreak;\n\n\t\t\t/* If we missed events, just flush out the current stack */\n\t\t\tif (record->missed_events)\n\t\t\t\tflush_stack();\n\n\t\t\tprocess_record(pevent, record);\n\t\t\ttracecmd_free_record(record);\n\t\t}\n\t}\n\n\tif (current_pid >= 0)\n\t\tsave_call_chain(current_pid, ips, ips_idx, 0);\n\tif (pending_pid >= 0)\n\t\tsave_call_chain(pending_pid, pending_ips, pending_ips_idx, 1);\n\n\tsave_stored_stacks();\n\n\tsort_chains();\n\tprint_chains(pevent);\n}\n\nvoid trace_hist(int argc, char **argv)\n{\n\tstruct tracecmd_input *handle;\n\tconst char *input_file = NULL;\n\tint instances;\n\tint ret;\n\n\tfor (;;) {\n\t\tint c;\n\n\t\tc = getopt(argc-1, argv+1, \"+hi:P\");\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\tif (input_file)\n\t\t\t\tdie(\"Only one input for historgram\");\n\t\t\tinput_file = optarg;\n\t\t\tbreak;\n\t\tcase 'P':\n\t\t\tcompact = 1;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif ((argc - optind) >= 2) {\n\t\tif (input_file)\n\t\t\tusage(argv);\n\t\tinput_file = argv[optind + 1];\n\t}\n\n\tif (!input_file)\n\t\tinput_file = DEFAULT_INPUT_FILE;\n\n\thandle = tracecmd_alloc(input_file, 0);\n\tif (!handle)\n\t\tdie(\"can't open %s\\n\", input_file);\n\n\tret = tracecmd_read_headers(handle, 0);\n\tif (ret) {\n\t\ttracecmd_close(handle);\n\t\treturn;\n\t}\n\n\tret = tracecmd_init_data(handle);\n\tif (ret < 0)\n\t\tdie(\"failed to init data\");\n\n\tif (ret > 0)\n\t\tdie(\"trace-cmd hist does not work with latency traces\\n\");\n\n\tinstances = tracecmd_buffer_instances(handle);\n\tif (instances) {\n\t\tstruct tracecmd_input *new_handle;\n\t\tint i;\n\n\t\tfor (i = 0; i < instances; i++) {\n\t\t\tnew_handle = tracecmd_buffer_instance_handle(handle, i);\n\t\t\tif (!new_handle) {\n\t\t\t\twarning(\"could not retrieve handle %d\", i);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tdo_trace_hist(new_handle);\n\t\t\ttracecmd_close(new_handle);\n\t\t}\n\t} else {\n\t\tdo_trace_hist(handle);\n\t}\n\n\ttracecmd_close(handle);\n}\n"
  },
  {
    "path": "tracecmd/trace-list.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"tracefs.h\"\n#include \"trace-local.h\"\n\n#define BTF_FILE\t\"/sys/kernel/btf/vmlinux\"\n\nstatic void dump_file_content(const char *path)\n{\n\tchar buf[BUFSIZ];\n\tssize_t n;\n\tFILE *fp;\n\n\tfp = fopen(path, \"r\");\n\tif (!fp)\n\t\tdie(\"reading %s\", path);\n\n\tdo {\n\t\tn = fread(buf, 1, BUFSIZ, fp);\n\t\tif (n > 0)\n\t\t\tfwrite(buf, 1, n, stdout);\n\t} while (n > 0);\n\tfclose(fp);\n}\n\n\n\nvoid show_instance_file(struct buffer_instance *instance, const char *name)\n{\n\tchar *path;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, name);\n\tdump_file_content(path);\n\ttracefs_put_tracing_file(path);\n}\n\nenum {\n\tSHOW_EVENT_FORMAT\t\t= 1 << 0,\n\tSHOW_EVENT_FILTER\t\t= 1 << 1,\n\tSHOW_EVENT_TRIGGER\t\t= 1 << 2,\n\tSHOW_EVENT_FULL\t\t\t= 1 << 3,\n};\n\n\nvoid show_file(const char *name)\n{\n\tchar *path;\n\n\tpath = tracefs_get_tracing_file(name);\n\tdump_file_content(path);\n\ttracefs_put_tracing_file(path);\n}\n\ntypedef int (*process_file_func)(char *buf, int len, int flags);\n\nstatic void process_file_re(process_file_func func,\n\t\t\t    const char *name, const char *re, int flags)\n{\n\tregex_t reg;\n\tchar *path;\n\tchar *buf = NULL;\n\tchar *str;\n\tFILE *fp;\n\tssize_t n;\n\tsize_t l = strlen(re);\n\n\t/* Just in case :-p */\n\tif (!re || l == 0) {\n\t\tshow_file(name);\n\t\treturn;\n\t}\n\n\t/* Handle the newline at end of names for the user */\n\tstr = malloc(l + 3);\n\tif (!str)\n\t\tdie(\"Failed to allocate reg ex %s\", re);\n\tstrcpy(str, re);\n\tif (re[l-1] == '$')\n\t\tstrcpy(&str[l-1], \"\\n*$\");\n\n\tif (regcomp(&reg, str, REG_ICASE|REG_NOSUB))\n\t\tdie(\"invalid function regex '%s'\", re);\n\n\tfree(str);\n\n\tpath = tracefs_get_tracing_file(name);\n\tfp = fopen(path, \"r\");\n\tif (!fp)\n\t\tdie(\"reading %s\", path);\n\ttracefs_put_tracing_file(path);\n\n\tdo {\n\t\tn = getline(&buf, &l, fp);\n\t\tif (n > 0 && regexec(&reg, buf, 0, NULL, 0) == 0)\n\t\t\tfunc(buf, n, flags);\n\t} while (n > 0);\n\tfree(buf);\n\tfclose(fp);\n\n\tregfree(&reg);\n}\n\nstatic void show_event(process_file_func func, const char *system,\n\t\t       const char *event, int flags)\n{\n\tchar *buf;\n\tint ret;\n\n\tret = asprintf(&buf, \"%s:%s\", system, event);\n\tif (ret < 0)\n\t\tdie(\"Can not allocate event\");\n\tfunc(buf, strlen(buf), flags);\n\tfree(buf);\n}\n\nstatic void show_system(process_file_func func, const char *system, int flags)\n{\n\tchar **events;\n\tint e;\n\n\tevents = tracefs_system_events(NULL, system);\n\tif (!events) /* die? */\n\t\treturn;\n\n\tfor (e = 0; events[e]; e++)\n\t\tshow_event(func, system, events[e], flags);\n}\n\nstatic void show_event_systems(process_file_func func, char **systems, int flags)\n{\n\tint s;\n\n\tfor (s = 0; systems[s]; s++)\n\t\tshow_system(func, systems[s], flags);\n}\n\nstatic void match_system_events(process_file_func func, const char *system,\n\t\t\t\tregex_t *reg, int flags)\n{\n\tchar **events;\n\tint e;\n\n\tevents = tracefs_system_events(NULL, system);\n\tif (!events) /* die? */\n\t\treturn;\n\tfor (e = 0; events[e]; e++) {\n\t\tif (regexec(reg, events[e], 0, NULL, 0) == 0)\n\t\t\tshow_event(func, system, events[e], flags);\n\t}\n\ttracefs_list_free(events);\n}\n\nstatic void process_events(process_file_func func, const char *re, int flags)\n{\n\tconst char *ftrace = \"ftrace\";\n\tregex_t system_reg;\n\tregex_t event_reg;\n\tchar *str;\n\tsize_t l = strlen(re);\n\tbool just_systems = true;\n\tchar **systems;\n\tchar *system;\n\tchar *event;\n\tint s;\n\n\tsystems = tracefs_event_systems(NULL);\n\tif (!systems)\n\t\treturn process_file_re(func, \"available_events\", re, flags);\n\n\tif (!re || l == 0) {\n\t\tshow_event_systems(func, systems, flags);\n\t\treturn;\n\t}\n\n\tstr = strdup(re);\n\tif (!str)\n\t\tdie(\"Can not allocate momory for regex\");\n\n\tsystem = strtok(str, \":\");\n\tevent = strtok(NULL, \"\");\n\n\tif (regcomp(&system_reg, system, REG_ICASE|REG_NOSUB))\n\t\tdie(\"invalid regex '%s'\", system);\n\n\tif (event) {\n\t\tif (regcomp(&event_reg, event, REG_ICASE|REG_NOSUB))\n\t\t\tdie(\"invalid regex '%s'\", event);\n\t} else {\n\t\t/*\n\t\t * If the regex ends with \":\", then event would be null,\n\t\t * but we do not want to match events.\n\t\t */\n\t\tif (re[l-1] != ':')\n\t\t\tjust_systems = false;\n\t}\n\tfree(str);\n\n\t/*\n\t * See if this matches the special ftrace system, as ftrace is not included\n\t * in the systems list, but can get events from tracefs_system_events().\n\t */\n\tif (regexec(&system_reg, ftrace, 0, NULL, 0) == 0) {\n\t\tif (!event)\n\t\t\tshow_system(func, ftrace, flags);\n\t\telse\n\t\t\tmatch_system_events(func, ftrace, &event_reg, flags);\n\t} else if (!just_systems) {\n\t\tmatch_system_events(func, ftrace, &system_reg, flags);\n\t}\n\n\tfor (s = 0; systems[s]; s++) {\n\n\t\tif (regexec(&system_reg, systems[s], 0, NULL, 0) == 0) {\n\t\t\tif (!event) {\n\t\t\t\tshow_system(func, systems[s], flags);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmatch_system_events(func, systems[s], &event_reg, flags);\n\t\t\tcontinue;\n\t\t}\n\t\tif (just_systems)\n\t\t\tcontinue;\n\n\t\tmatch_system_events(func, systems[s], &system_reg, flags);\n\t}\n\ttracefs_list_free(systems);\n\n\tregfree(&system_reg);\n\tif (event)\n\t\tregfree(&event_reg);\n}\n\nstatic char *get_event_file(const char *type, char *buf, int len)\n{\n\tchar *system;\n\tchar *event;\n\tchar *path;\n\tchar *file;\n\tint ret;\n\n\tif (buf[len-1] == '\\n')\n\t\tbuf[len-1] = '\\0';\n\n\tsystem = strtok(buf, \":\");\n\tif (!system)\n\t\tdie(\"no system found in %s\", buf);\n\n\tevent = strtok(NULL, \":\");\n\tif (!event)\n\t\tdie(\"no event found in %s\\n\", buf);\n\n\tpath = tracefs_get_tracing_file(\"events\");\n\tret = asprintf(&file, \"%s/%s/%s/%s\", path, system, event, type);\n\tif (ret < 0)\n\t\tdie(\"Failed to allocate event file %s %s\", system, event);\n\n\ttracefs_put_tracing_file(path);\n\n\treturn file;\n}\n\nstatic int event_filter_write(char *buf, int len, int flags)\n{\n\tchar *file;\n\n\tif (buf[len-1] == '\\n')\n\t\tbuf[len-1] = '\\0';\n\n\tprintf(\"%s\\n\", buf);\n\n\tfile = get_event_file(\"filter\", buf, len);\n\tdump_file_content(file);\n\tfree(file);\n\tprintf(\"\\n\");\n\n\treturn 0;\n}\n\nstatic int event_trigger_write(char *buf, int len, int flags)\n{\n\tchar *file;\n\n\tif (buf[len-1] == '\\n')\n\t\tbuf[len-1] = '\\0';\n\n\tprintf(\"%s\\n\", buf);\n\n\tfile = get_event_file(\"trigger\", buf, len);\n\tdump_file_content(file);\n\tfree(file);\n\tprintf(\"\\n\");\n\n\treturn 0;\n}\n\nstatic int event_format_write(char *fbuf, int len, int flags)\n{\n\tchar *file = get_event_file(\"format\", fbuf, len);\n\tchar *buf = NULL;\n\tsize_t l;\n\tFILE *fp;\n\tbool full;\n\tint n;\n\n\tfull = flags & SHOW_EVENT_FULL;\n\n\t/* The get_event_file() crops system in fbuf */\n\tprintf(\"system: %s\\n\", fbuf);\n\n\t/* Don't print the print fmt, it's ugly */\n\n\tfp = fopen(file, \"r\");\n\tif (!fp)\n\t\tdie(\"reading %s\", file);\n\n\tdo {\n\t\tn = getline(&buf, &l, fp);\n\t\tif (n > 0) {\n\t\t\tif (!full && strncmp(buf, \"print fmt\", 9) == 0)\n\t\t\t\tbreak;\n\t\t\tfwrite(buf, 1, n, stdout);\n\t\t}\n\t} while (n > 0);\n\tfclose(fp);\n\tfree(buf);\n\tfree(file);\n\n\treturn 0;\n}\n\nstatic int event_name(char *buf, int len, int flags)\n{\n\tprintf(\"%s\\n\", buf);\n\n\treturn 0;\n}\n\nstatic void show_event_filter_re(const char *re)\n{\n\tprocess_events(event_filter_write, re, 0);\n}\n\n\nstatic void show_event_trigger_re(const char *re)\n{\n\tprocess_events(event_trigger_write, re, 0);\n}\n\n\nstatic void show_event_format_re(const char *re, int flags)\n{\n\tprocess_events(event_format_write, re, flags);\n}\n\nstatic void show_event_names_re(const char *re)\n{\n\tprocess_events(event_name, re, 0);\n}\n\nstatic void show_events(const char *eventre, int flags)\n{\n\tif (flags && !eventre)\n\t\tdie(\"When specifying event files, an event must be named\");\n\n\tif (eventre) {\n\t\tif (flags & SHOW_EVENT_FORMAT)\n\t\t\tshow_event_format_re(eventre, flags);\n\n\t\telse if (flags & SHOW_EVENT_FILTER)\n\t\t\tshow_event_filter_re(eventre);\n\n\t\telse if (flags & SHOW_EVENT_TRIGGER)\n\t\t\tshow_event_trigger_re(eventre);\n\t\telse\n\t\t\tshow_event_names_re(eventre);\n\t} else\n\t\tshow_file(\"available_events\");\n}\n\n\nstatic void show_tracers(void)\n{\n\tshow_file(\"available_tracers\");\n}\n\nvoid show_options(const char *prefix, struct buffer_instance *buffer, const char *re)\n{\n\tstruct tracefs_instance *instance = buffer ? buffer->tracefs : NULL;\n\tstruct dirent *dent;\n\tstruct stat st;\n\tregex_t reg;\n\tchar *path;\n\tDIR *dir;\n\n\tif (re && strlen(re)) {\n\t\tif (regcomp(&reg, re, REG_ICASE|REG_NOSUB))\n\t\t\tdie(\"invalid function regex '%s'\", re);\n\t} else {\n\t\tre = NULL;\n\t}\n\n\tif (!prefix)\n\t\tprefix = \"\";\n\n\tpath = tracefs_instance_get_file(instance, \"options\");\n\tif (!path)\n\t\tgoto show_file;\n\tif (stat(path, &st) < 0)\n\t\tgoto show_file;\n\n\tif ((st.st_mode & S_IFMT) != S_IFDIR)\n\t\tgoto show_file;\n\n\tdir = opendir(path);\n\tif (!dir)\n\t\tdie(\"Can not read instance directory\");\n\n\twhile ((dent = readdir(dir))) {\n\t\tconst char *name = dent->d_name;\n\t\tlong long val;\n\t\tchar *file;\n\t\tint ret;\n\n\t\tif (strcmp(name, \".\") == 0 ||\n\t\t    strcmp(name, \"..\") == 0)\n\t\t\tcontinue;\n\n\t\tif (re && regexec(&reg, name, 0, NULL, 0) != 0)\n\t\t\tcontinue;\n\n\t\tret = asprintf(&file, \"options/%s\", name);\n\t\tif (ret < 0)\n\t\t\tdie(\"Failed to allocate file name\");\n\t\tret = tracefs_instance_file_read_number(instance, file, &val);\n\t\tif (!ret) {\n\t\t\tif (val)\n\t\t\t\tprintf(\"%s%s\\n\", prefix, name);\n\t\t\telse\n\t\t\t\tprintf(\"%sno%s\\n\", prefix, name);\n\t\t}\n\t\tfree(file);\n\t}\n\tclosedir(dir);\n\ttracefs_put_tracing_file(path);\n\treturn;\n\n show_file:\n\ttracefs_put_tracing_file(path);\n\tshow_file(\"trace_options\");\n}\n\nstatic void show_clocks(void)\n{\n\tchar *clocks;\n\tint size;\n\n\tclocks = tracefs_instance_file_read(NULL, \"trace_clock\", &size);\n\tif (!clocks)\n\t\tdie(\"getting clocks\");\n\tif (clocks[size - 1] == '\\n')\n\t\tclocks[size - 1] = 0;\n\n\tif (trace_tsc2nsec_is_supported())\n\t\tprintf(\"%s %s\\n\", clocks, TSCNSEC_CLOCK);\n\telse\n\t\tprintf(\"%s\\n\", clocks);\n\n\tfree(clocks);\n}\n\nstatic struct tep_handle *load_btf(void)\n{\n\tstruct tep_handle *tep;\n\tstruct stat st;\n\tssize_t size;\n\tchar *buf;\n\tint ret;\n\tint fd;\n\tint r, s = 0;\n\n\tret = stat(BTF_FILE, &st);\n\tif (ret < 0)\n\t\treturn NULL;\n\n\tsize = st.st_size;\n\tbuf = malloc(size);\n\tif (!buf)\n\t\treturn NULL;\n\n\tfd = open(BTF_FILE, O_RDONLY);\n\tif (fd < 0)\n\t\treturn NULL;\n\n\twhile (size) {\n\t\tr = read(fd, buf + s, size);\n\t\tif (r < 0)\n\t\t\tbreak;\n\t\ts += r;\n\t\tsize -= r;\n\t}\n\tclose(fd);\n\n\ttep = tep_alloc();\n\n\tif (!tep || tep_load_btf(tep, buf, s) < 0) {\n\t\ttep_free(tep);\n\t\ttep = NULL;\n\t}\n\n\tfree(buf);\n\treturn tep;\n}\n\nstatic void show_functions(const char *funcre, int params)\n{\n\tstruct tep_handle *tep = NULL;\n\tstruct trace_seq s;\n\tbool found = false;\n\tchar *new_re = NULL;\n\tchar **list;\n\tint i;\n\n\tif (!funcre) {\n\t\tif (!params) {\n\t\t\tshow_file(\"available_filter_functions\");\n\t\t\treturn;\n\t\t}\n\t\tfuncre = \"^.*\";\n\t}\n\n\ttrace_seq_init(&s);\n\tif (params) {\n\t\ttep = load_btf();\n\t\tif (!tep)\n\t\t\tparams = 0;\n\t}\n\n\t/* if the re doesn't have any regular expressions, then add them */\n\tfor (i = 0; !found && funcre[i]; i++) {\n\t\tif (funcre[i] == '\\\\')\n\t\t\tcontinue;\n\t\tswitch (funcre[i]) {\n\t\tcase '*':\n\t\tcase '[':\n\t\tcase '(':\n\t\tcase '.':\n\t\tcase '?':\n\t\t\tfound = true;\n\t\t}\n\t}\n\n\tif (!found) {\n\t\t/* Add glob around expression */\n\t\tif (asprintf(&new_re, \"*%s*\", funcre) < 0)\n\t\t\tdie(\"Failed to allocate memory\");\n\t\tfuncre = new_re;\n\t}\n\n\tif (tracefs_filter_functions(funcre, NULL, &list) < 0)\n\t\tdie(\"Failed to read filte functions\");\n\tfor (i = 0; list && list[i]; i++) {\n\t\tprintf(\"%s\", list[i]);\n\t\tif (params) {\n\t\t\ttrace_seq_reset(&s);\n\t\t\tif (tep_btf_list_args(tep, &s, list[i]) >= 0) {\n\t\t\t\tprintf(\"(\");\n\t\t\t\ttrace_seq_do_printf(&s);\n\t\t\t\tprintf(\")\");\n\t\t\t}\n\t\t}\n\t\tprintf(\"\\n\");\n\t}\n\ttracefs_list_free(list);\n\tfree(new_re);\n}\n\n\nstatic void show_buffers(void)\n{\n\tchar **list;\n\tint i;\n\n\tlist = tracefs_instances(NULL);\n\tif (!list)\n\t\tdie(\"Can not read instance directory\");\n\n\tfor (i = 0; list[i]; i++)\n\t\tprintf(\"%s\\n\", list[i]);\n\n\tif (!i)\n\t\tprintf(\"No buffer instances defined\\n\");\n\n\ttracefs_list_free(list);\n}\n\n\nstatic void show_systems(void)\n{\n\tstruct dirent *dent;\n\tchar *path;\n\tDIR *dir;\n\n\tpath = tracefs_get_tracing_file(\"events\");\n\tdir = opendir(path);\n\n\tif (!dir)\n\t\tdie(\"Can not read events directory\");\n\n\twhile ((dent = readdir(dir))) {\n\t\tconst char *name = dent->d_name;\n\t\tstruct stat st;\n\t\tchar *spath;\n\t\tint ret;\n\n\t\tif (strcmp(name, \".\") == 0 ||\n\t\t    strcmp(name, \"..\") == 0)\n\t\t\tcontinue;\n\n\t\tif (asprintf(&spath, \"%s/%s\", path, name) < 0)\n\t\t\tcontinue;\n\t\tret = stat(spath, &st);\n\t\tif (!ret && S_ISDIR(st.st_mode))\n\t\t\tprintf(\"%s\\n\", name);\n\n\t\tfree(spath);\n\t}\n\n\tprintf(\"\\n\");\n\tclosedir(dir);\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void show_plugin_options(void)\n{\n\tstruct tep_handle *pevent;\n\tstruct tep_plugin_list *list;\n\tstruct trace_seq s;\n\n\ttracecmd_ftrace_load_options();\n\n\tpevent = tep_alloc();\n\tif (!pevent)\n\t\tdie(\"Can not allocate pevent\\n\");\n\n\ttrace_seq_init(&s);\n\n\tlist = tcmd_load_plugins(pevent, 0);\n\ttep_plugin_print_options(&s);\n\ttrace_seq_do_printf(&s);\n\ttep_unload_plugins(list, pevent);\n\ttep_free(pevent);\n}\n\n\nvoid trace_option(int argc, char **argv)\n{\n\tshow_plugin_options();\n}\n\n\nstatic void show_plugins(void)\n{\n\tstruct tep_handle *pevent;\n\tstruct tep_plugin_list *list;\n\tstruct trace_seq s;\n\n\tpevent = tep_alloc();\n\tif (!pevent)\n\t\tdie(\"Can not allocate pevent\\n\");\n\n\ttrace_seq_init(&s);\n\n\tlist = tcmd_load_plugins(pevent, 0);\n\ttep_print_plugins(&s, \"  \", \"\\n\", list);\n\n\ttrace_seq_do_printf(&s);\n\ttep_unload_plugins(list, pevent);\n\ttep_free(pevent);\n}\n\nstatic void show_compression(void)\n{\n\tchar **versions, **names;\n\tint c, i;\n\n\tc = tracecmd_compress_protos_get(&names, &versions);\n\tif (c <= 0) {\n\t\tprintf(\"No compression algorithms are supported\\n\");\n\t\treturn;\n\t}\n\tprintf(\"Supported compression algorithms:\\n\");\n\tfor (i = 0; i < c; i++)\n\t\tprintf(\"\\t%s, %s\\n\", names[i], versions[i]);\n\n\tfree(names);\n\tfree(versions);\n}\n\nvoid trace_list(int argc, char **argv)\n{\n\tint events = 0;\n\tint tracer = 0;\n\tint options = 0;\n\tint funcs = 0;\n\tint buffers = 0;\n\tint clocks = 0;\n\tint plug = 0;\n\tint plug_op = 0;\n\tint flags = 0;\n\tint systems = 0;\n\tint show_all = 1;\n\tint compression = 0;\n\tint params = 0;\n\tint i;\n\tconst char *arg;\n\tconst char *funcre = NULL;\n\tconst char *eventre = NULL;\n\tconst char *optionre = NULL;\n\n\tfor (i = 2; i < argc; i++) {\n\t\targ = NULL;\n\t\tif (argv[i][0] == '-') {\n\t\t\tif (i < argc - 1) {\n\t\t\t\tif (argv[i+1][0] != '-')\n\t\t\t\t\targ = argv[i+1];\n\t\t\t}\n\t\t\tswitch (argv[i][1]) {\n\t\t\tcase 'h':\n\t\t\t\tusage(argv);\n\t\t\t\tbreak;\n\t\t\tcase 'e':\n\t\t\t\tevents = 1;\n\t\t\t\teventre = arg;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'B':\n\t\t\t\tbuffers = 1;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'C':\n\t\t\t\tclocks = 1;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'F':\n\t\t\t\tflags |= SHOW_EVENT_FORMAT;\n\t\t\t\tbreak;\n\t\t\tcase 'R':\n\t\t\t\tflags |= SHOW_EVENT_TRIGGER;\n\t\t\t\tbreak;\n\t\t\tcase 'l':\n\t\t\t\tflags |= SHOW_EVENT_FILTER;\n\t\t\t\tbreak;\n\t\t\tcase 'p':\n\t\t\tcase 't':\n\t\t\t\ttracer = 1;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'P':\n\t\t\t\tplug = 1;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'O':\n\t\t\t\tplug_op = 1;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'o':\n\t\t\t\toptions = 1;\n\t\t\t\toptionre = arg;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'f':\n\t\t\t\tfuncs = 1;\n\t\t\t\tfuncre = arg;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\tsystems = 1;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'c':\n\t\t\t\tcompression = 1;\n\t\t\t\tshow_all = 0;\n\t\t\t\tbreak;\n\t\t\tcase '-':\n\t\t\t\tif (strcmp(argv[i], \"--debug\") == 0) {\n\t\t\t\t\ttracecmd_set_debug(true);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (strcmp(argv[i], \"--full\") == 0) {\n\t\t\t\t\tflags |= SHOW_EVENT_FULL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (strcmp(argv[i], \"--proto\") == 0) {\n\t\t\t\t\tparams = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfprintf(stderr, \"list: invalid option -- '%s'\\n\",\n\t\t\t\t\targv[i]);\n\t\t\tdefault:\n\t\t\t\tfprintf(stderr, \"list: invalid option -- '%c'\\n\",\n\t\t\t\t\targv[i][1]);\n\t\t\t\tusage(argv);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (events)\n\t\tshow_events(eventre, flags);\n\n\tif (tracer)\n\t\tshow_tracers();\n\n\tif (options)\n\t\tshow_options(NULL, NULL, optionre);\n\n\tif (plug)\n\t\tshow_plugins();\n\n\tif (plug_op)\n\t\tshow_plugin_options();\n\n\tif (funcs)\n\t\tshow_functions(funcre, params);\n\n\tif (buffers)\n\t\tshow_buffers();\n\n\tif (clocks)\n\t\tshow_clocks();\n\tif (systems)\n\t\tshow_systems();\n\tif (compression)\n\t\tshow_compression();\n\tif (show_all) {\n\t\tprintf(\"event systems:\\n\");\n\t\tshow_systems();\n\t\tprintf(\"events:\\n\");\n\t\tshow_events(NULL, 0);\n\t\tprintf(\"\\ntracers:\\n\");\n\t\tshow_tracers();\n\t\tprintf(\"\\noptions:\\n\");\n\t\tshow_options(NULL, NULL, NULL);\n\t\tshow_compression();\n\t}\n\n\treturn;\n\n}\n"
  },
  {
    "path": "tracecmd/trace-listen.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <limits.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/wait.h>\n#include <netdb.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <signal.h>\n#include <errno.h>\n\n#ifdef VSOCK\n#include <linux/vm_sockets.h>\n#endif\n\n#include \"trace-local.h\"\n#include \"trace-msg.h\"\n\n#define dprint(fmt, ...)\ttracecmd_debug(fmt, ##__VA_ARGS__)\n\n#define MAX_OPTION_SIZE 4096\n\n#define _VAR_DIR_Q(dir)\t\t#dir\n#define VAR_DIR_Q(dir)\t\t_VAR_DIR_Q(dir)\n\n#define VAR_RUN_DIR\t\tVAR_DIR_Q(VAR_DIR) \"/run\"\n\n#define LISTEN_PIDFILE\t\t\"trace-cmd-net.pid\"\n\nstatic char *default_output_dir = \".\";\nstatic char *output_dir;\nstatic char *default_output_file = \"trace\";\nstatic char *output_file;\n\nstatic bool use_vsock;\n\nstatic int backlog = 5;\n\nstatic int do_daemon;\n\n/* Used for signaling INT to finish */\nstatic struct tracecmd_msg_handle *stop_msg_handle;\nstatic bool done;\n\n#define pdie(fmt, ...)\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\\\n\t\ttracecmd_plog_error(fmt, ##__VA_ARGS__);\\\n\t\tif (do_daemon)\t\t\t\t\\\n\t\t\tremove_pid_file(LISTEN_PIDFILE);\\\n\t\texit(-1);\t\t\t\t\\\n\t} while (0)\n\n#define  TEMP_FILE_STR \"%s.%s:%s.cpu%d\", output_file, host, port, cpu\nstatic char *get_temp_file(const char *host, const char *port, int cpu)\n{\n\tchar *file = NULL;\n\tint size;\n\n\tsize = snprintf(file, 0, TEMP_FILE_STR);\n\tfile = malloc(size + 1);\n\tif (!file)\n\t\treturn NULL;\n\tsprintf(file, TEMP_FILE_STR);\n\n\treturn file;\n}\n\nstatic void put_temp_file(char *file)\n{\n\tfree(file);\n}\n\nstatic void signal_setup(int sig, sighandler_t handle)\n{\n\tstruct sigaction action;\n\n\tsigaction(sig, NULL, &action);\n\t/* Make accept return EINTR */\n\taction.sa_flags &= ~SA_RESTART;\n\taction.sa_handler = handle;\n\tsigaction(sig, &action, NULL);\n}\n\nstatic void delete_temp_file(const char *host, const char *port, int cpu)\n{\n\tchar file[PATH_MAX];\n\n\tsnprintf(file, PATH_MAX, TEMP_FILE_STR);\n\tunlink(file);\n}\n\nstatic int read_string(int fd, char *buf, size_t size)\n{\n\tsize_t i;\n\tint n;\n\n\tfor (i = 0; i < size; i++) {\n\t\tn = read(fd, buf+i, 1);\n\t\tif (!buf[i] || n <= 0)\n\t\t\tbreak;\n\t}\n\n\treturn i;\n}\n\nstatic int process_option(struct tracecmd_msg_handle *msg_handle, char *option)\n{\n\t/* currently the only option we have is to us TCP */\n\tif (strcmp(option, \"TCP\") == 0) {\n\t\tmsg_handle->flags |= TRACECMD_MSG_FL_USE_TCP;\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic void finish(int sig)\n{\n\tif (stop_msg_handle)\n\t\ttracecmd_msg_set_done(stop_msg_handle);\n\tdone = true;\n}\n\nvoid make_pid_name(char *buf, const char *pidfile_basename)\n{\n\tsnprintf(buf, PATH_MAX, VAR_RUN_DIR \"/%s\", pidfile_basename);\n}\n\nvoid remove_pid_file(const char *pidfile_basename)\n{\n\tchar buf[PATH_MAX];\n\n\tmake_pid_name(buf, pidfile_basename);\n\tunlink(buf);\n}\n\nstatic int process_child(int sfd, const char *host, const char *port,\n\t\t\t int cpu, int page_size, enum port_type type)\n{\n\tstruct sockaddr_storage peer_addr;\n#ifdef VSOCK\n\tstruct sockaddr_vm vm_addr;\n#endif\n\tstruct sockaddr *addr;\n\tsocklen_t addr_len;\n\tchar buf[page_size];\n\tchar *tempfile;\n\tint left;\n\tint cfd;\n\tint fd;\n\tint r, w;\n\tint once = 0;\n\n\tsignal_setup(SIGUSR1, finish);\n\n\ttempfile = get_temp_file(host, port, cpu);\n\tif (!tempfile)\n\t\treturn -ENOMEM;\n\n\tfd = open(tempfile, O_WRONLY | O_TRUNC | O_CREAT, 0644);\n\tif (fd < 0)\n\t\tpdie(\"creating %s\", tempfile);\n\n\tif (type == USE_TCP) {\n\t\taddr = (struct sockaddr *)&peer_addr;\n\t\taddr_len = sizeof(peer_addr);\n#ifdef VSOCK\n\t} else if (type == USE_VSOCK) {\n\t\taddr = (struct sockaddr *)&vm_addr;\n\t\taddr_len = sizeof(vm_addr);\n#endif\n\t}\n\n\tif (type == USE_TCP || type == USE_VSOCK) {\n\t\tif (listen(sfd, backlog) < 0)\n\t\t\tpdie(\"listen\");\n\n\t\tcfd = accept(sfd, addr, &addr_len);\n\t\tif (cfd < 0 && errno == EINTR)\n\t\t\tgoto done;\n\t\tif (cfd < 0)\n\t\t\tpdie(\"accept\");\n\t\tclose(sfd);\n\t\tsfd = cfd;\n\t}\n\n\tfor (;;) {\n\t\t/* TODO, make this copyless! */\n\t\tr = read(sfd, buf, page_size);\n\t\tif (r < 0) {\n\t\t\tif (errno == EINTR)\n\t\t\t\tbreak;\n\t\t\tpdie(\"reading pages from client\");\n\t\t}\n\t\tif (!r)\n\t\t\tbreak;\n\t\t/* UDP requires that we get the full size in one go */\n\t\tif (type == USE_UDP && r < page_size && !once) {\n\t\t\tonce = 1;\n\t\t\twarning(\"read %d bytes, expected %d\", r, page_size);\n\t\t}\n\n\t\tleft = r;\n\t\tdo {\n\t\t\tw = write(fd, buf + (r - left), left);\n\t\t\tif (w > 0)\n\t\t\t\tleft -= w;\n\t\t} while (w >= 0 && left);\n\t}\n\n done:\n\tput_temp_file(tempfile);\n\texit(0);\n}\n\nstatic int setup_vsock_port(int start_port, int *sfd)\n{\n\tint sd;\n\n\tsd = tcmd_vsock_make(start_port);\n\tif (sd < 0)\n\t\treturn -errno;\n\t*sfd = sd;\n\n\treturn start_port;\n}\n\nint trace_net_make(int port, enum port_type type)\n{\n\tstruct addrinfo hints;\n\tstruct addrinfo *result, *rp;\n\tchar buf[BUFSIZ];\n\tint sd;\n\tint s;\n\n\tsnprintf(buf, BUFSIZ, \"%d\", port);\n\n\tmemset(&hints, 0, sizeof(hints));\n\thints.ai_family = AF_UNSPEC;\n\thints.ai_flags = AI_PASSIVE;\n\n\tswitch (type) {\n\tcase USE_TCP:\n\t\thints.ai_socktype = SOCK_STREAM;\n\t\tbreak;\n\tcase USE_UDP:\n\t\thints.ai_socktype = SOCK_DGRAM;\n\t\tbreak;\n\tdefault:\n\t\treturn -1;\n\t}\n\n\ts = getaddrinfo(NULL, buf, &hints, &result);\n\tif (s != 0)\n\t\tpdie(\"getaddrinfo: error opening socket\");\n\n\tfor (rp = result; rp != NULL; rp = rp->ai_next) {\n\t\tsd = socket(rp->ai_family, rp->ai_socktype,\n\t\t\t    rp->ai_protocol);\n\t\tif (sd < 0)\n\t\t\tcontinue;\n\n\t\tset_tcp_no_delay(sd, rp->ai_socktype);\n\t\tif (bind(sd, rp->ai_addr, rp->ai_addrlen) == 0)\n\t\t\tbreak;\n\n\t\tclose(sd);\n\t}\n\tfreeaddrinfo(result);\n\n\tif (rp == NULL)\n\t\treturn -1;\n\n\tdprint(\"Create listen port: %d fd:%d\\n\", port, sd);\n\n\treturn sd;\n}\n\nint trace_net_search(int start_port, int *sfd, enum port_type type)\n{\n\tint num_port = start_port;\n\n\tif (type == USE_VSOCK)\n\t\treturn setup_vsock_port(start_port, sfd);\n again:\n\t*sfd = trace_net_make(num_port, type);\n\tif (*sfd < 0) {\n\t\tif (++num_port > MAX_PORT_SEARCH)\n\t\t\tpdie(\"No available ports to bind\");\n\t\tgoto again;\n\t}\n\n\treturn num_port;\n}\n\nstatic void fork_reader(int sfd, const char *node, const char *port,\n\t\t\tint *pid, int cpu, int pagesize, enum port_type type)\n{\n\tint ret;\n\n\t*pid = fork();\n\n\tif (*pid < 0)\n\t\tpdie(\"creating reader\");\n\n\tif (!*pid) {\n\t\tret = process_child(sfd, node, port, cpu, pagesize, type);\n\t\tif (ret < 0)\n\t\t\tpdie(\"Problem with reader %d\", ret);\n\t}\n\n\tclose(sfd);\n}\n\nstatic int open_port(const char *node, const char *port, int *pid,\n\t\t     int cpu, int pagesize, int start_port, enum port_type type)\n{\n\tint sfd;\n\tint num_port;\n\n\t/*\n\t * trace_net_search() currently does not return an error, but if that\n\t * changes in the future, we have a check for it now.\n\t */\n\tnum_port = trace_net_search(start_port, &sfd, type);\n\tif (num_port < 0)\n\t\treturn num_port;\n\n\tfork_reader(sfd, node, port, pid, cpu, pagesize, type);\n\n\treturn num_port;\n}\n\nstatic int communicate_with_client(struct tracecmd_msg_handle *msg_handle)\n{\n\tchar *last_proto = NULL;\n\tchar buf[BUFSIZ];\n\tchar *option;\n\tint pagesize = 0;\n\tint options;\n\tint size;\n\tint cpus;\n\tint n, s, t, i;\n\tint ret = -EINVAL;\n\tint fd = msg_handle->fd;\n\n\t/* Let the client know what we are */\n\twrite(fd, \"tracecmd\", 8);\n\n try_again:\n\t/* read back the CPU count */\n\tn = read_string(fd, buf, BUFSIZ);\n\tif (n == BUFSIZ)\n\t\t/** ERROR **/\n\t\treturn -EINVAL;\n\n\tcpus = atoi(buf);\n\n\t/* Is the client using the new protocol? */\n\tif (cpus == -1) {\n\t\tif (memcmp(buf, V3_CPU, n) != 0) {\n\t\t\t/* If it did not send a version, then bail */\n\t\t\tif (memcmp(buf, \"-1V\", 3)) {\n\t\t\t\ttracecmd_plog(\"Unknown string %s\\n\", buf);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\t/* Skip \"-1\" */\n\t\t\ttracecmd_plog(\"Cannot handle the protocol %s\\n\", buf+2);\n\n\t\t\t/* If it returned the same command as last time, bail! */\n\t\t\tif (last_proto && strncmp(last_proto, buf, n) == 0) {\n\t\t\t\ttracecmd_plog(\"Repeat of version %s sent\\n\", last_proto);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tfree(last_proto);\n\t\t\tlast_proto = malloc(n + 1);\n\t\t\tif (last_proto) {\n\t\t\t\tmemcpy(last_proto, buf, n);\n\t\t\t\tlast_proto[n] = 0;\n\t\t\t}\n\t\t\t/* Return the highest protocol we can use */\n\t\t\twrite(fd, \"V3\", 3);\n\t\t\tgoto try_again;\n\t\t}\n\n\t\t/* Let the client know we use v3 protocol */\n\t\twrite(fd, \"V3\", 3);\n\n\t\t/* read the rest of dummy data */\n\t\tn = read(fd, buf, sizeof(V3_MAGIC));\n\t\tif (memcmp(buf, V3_MAGIC, n) != 0)\n\t\t\tgoto out;\n\n\t\t/* We're off! */\n\t\twrite(fd, \"OK\", 2);\n\n\t\tmsg_handle->version = V3_PROTOCOL;\n\n\t\t/* read the CPU count, the page size, and options */\n\t\tif ((pagesize = tracecmd_msg_initial_setting(msg_handle)) < 0)\n\t\t\tgoto out;\n\t} else {\n\t\t/* The client is using the v1 protocol */\n\n\t\ttracecmd_plog(\"cpus=%d\\n\", cpus);\n\t\tif (cpus < 0)\n\t\t\tgoto out;\n\n\t\tmsg_handle->cpu_count = cpus;\n\n\t\t/* next read the page size */\n\t\tn = read_string(fd, buf, BUFSIZ);\n\t\tif (n == BUFSIZ)\n\t\t\t/** ERROR **/\n\t\t\tgoto out;\n\n\t\tpagesize = atoi(buf);\n\n\t\ttracecmd_plog(\"pagesize=%d\\n\", pagesize);\n\t\tif (pagesize <= 0)\n\t\t\tgoto out;\n\n\t\t/* Now the number of options */\n\t\tn = read_string(fd, buf, BUFSIZ);\n \t\tif (n == BUFSIZ)\n\t\t\t/** ERROR **/\n\t\t\treturn -EINVAL;\n\n\t\toptions = atoi(buf);\n\n\t\tfor (i = 0; i < options; i++) {\n\t\t\t/* next is the size of the options */\n\t\t\tn = read_string(fd, buf, BUFSIZ);\n\t\t\tif (n == BUFSIZ)\n\t\t\t\t/** ERROR **/\n\t\t\t\tgoto out;\n\t\t\tsize = atoi(buf);\n\t\t\t/* prevent a client from killing us */\n\t\t\tif (size > MAX_OPTION_SIZE)\n\t\t\t\tgoto out;\n\n\t\t\tret = -ENOMEM;\n\t\t\toption = malloc(size);\n\t\t\tif (!option)\n\t\t\t\tgoto out;\n\n\t\t\tret = -EIO;\n\t\t\tt = size;\n\t\t\ts = 0;\n\t\t\tdo {\n\t\t\t\ts = read(fd, option+s, t);\n\t\t\t\tif (s <= 0) {\n\t\t\t\t\tfree(option);\n\t\t\t\t\tgoto out;\n\t\t\t\t}\n\t\t\t\tt -= s;\n\t\t\t\ts = size - t;\n\t\t\t} while (t);\n\n\t\t\ts = process_option(msg_handle, option);\n\t\t\tfree(option);\n\t\t\t/* do we understand this option? */\n\t\t\tret = -EINVAL;\n\t\t\tif (!s)\n\t\t\t\tgoto out;\n\t\t}\n\t}\n\n\tif (msg_handle->flags & TRACECMD_MSG_FL_USE_TCP)\n\t\ttracecmd_plog(\"Using TCP for live connection\\n\");\n\n\tret = pagesize;\n out:\n\tfree(last_proto);\n\n\treturn ret;\n}\n\nstatic int create_client_file(const char *node, const char *port)\n{\n\tchar buf[BUFSIZ];\n\tint ofd;\n\n\tsnprintf(buf, BUFSIZ, \"%s.%s:%s.dat\", output_file, node, port);\n\n\tofd = open(buf, O_RDWR | O_CREAT | O_TRUNC, 0644);\n\tif (ofd < 0)\n\t\tpdie(\"Can not create file %s\", buf);\n\treturn ofd;\n}\n\nstatic void destroy_all_readers(int cpus, int *pid_array, const char *node,\n\t\t\t\tconst char *port)\n{\n\tint cpu;\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tif (pid_array[cpu] > 0) {\n\t\t\tkill(pid_array[cpu], SIGKILL);\n\t\t\twaitpid(pid_array[cpu], NULL, 0);\n\t\t\tdelete_temp_file(node, port, cpu);\n\t\t\tpid_array[cpu] = 0;\n\t\t}\n\t}\n\n\tfree(pid_array);\n}\n\nstatic int *create_all_readers(const char *node, const char *port,\n\t\t\t       int pagesize, struct tracecmd_msg_handle *msg_handle)\n{\n\tenum port_type port_type = USE_UDP;\n\tchar buf[BUFSIZ];\n\tunsigned int *port_array;\n\tint *pid_array;\n\tunsigned int start_port;\n\tunsigned int connect_port;\n\tint cpus = msg_handle->cpu_count;\n\tint cpu;\n\tint pid;\n\n\tif (!pagesize)\n\t\treturn NULL;\n\n\tif (msg_handle->flags & TRACECMD_MSG_FL_USE_TCP)\n\t\tport_type = USE_TCP;\n\telse if (msg_handle->flags & TRACECMD_MSG_FL_USE_VSOCK)\n\t\tport_type = USE_VSOCK;\n\n\tport_array = malloc(sizeof(*port_array) * cpus);\n\tif (!port_array)\n\t\treturn NULL;\n\n\tpid_array = malloc(sizeof(*pid_array) * cpus);\n\tif (!pid_array) {\n\t\tfree(port_array);\n\t\treturn NULL;\n\t}\n\n\tmemset(pid_array, 0, sizeof(int) * cpus);\n\n\tstart_port = START_PORT_SEARCH;\n\n\t/* Now create a port for each CPU */\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tconnect_port = open_port(node, port, &pid, cpu,\n\t\t\t\t\t pagesize, start_port, port_type);\n\t\tif (connect_port < 0)\n\t\t\tgoto out_free;\n\t\tport_array[cpu] = connect_port;\n\t\tpid_array[cpu] = pid;\n\t\t/*\n\t\t * Due to some bugging finding ports,\n\t\t * force search after last port\n\t\t */\n\t\tstart_port = connect_port + 1;\n\t}\n\n\tif (msg_handle->version == V3_PROTOCOL) {\n\t\t/* send set of port numbers to the client */\n\t\tif (tracecmd_msg_send_port_array(msg_handle, port_array) < 0) {\n\t\t\ttracecmd_plog(\"Failed sending port array\\n\");\n\t\t\tgoto out_free;\n\t\t}\n\t} else {\n\t\t/* send the client a comma deliminated set of port numbers */\n\t\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\t\tsnprintf(buf, BUFSIZ, \"%s%d\",\n\t\t\t\t cpu ? \",\" : \"\", port_array[cpu]);\n\t\t\twrite(msg_handle->fd, buf, strlen(buf));\n\t\t}\n\t\t/* end with null terminator */\n\t\twrite(msg_handle->fd, \"\\0\", 1);\n\t}\n\n\tfree(port_array);\n\treturn pid_array;\n\n out_free:\n\tfree(port_array);\n\tdestroy_all_readers(cpus, pid_array, node, port);\n\treturn NULL;\n}\n\nstatic int\ncollect_metadata_from_client(struct tracecmd_msg_handle *msg_handle,\n\t\t\t     int ofd)\n{\n\tchar buf[BUFSIZ];\n\tint n, s, t;\n\tint ifd = msg_handle->fd;\n\tint ret = 0;\n\n\tdo {\n\t\tn = read(ifd, buf, BUFSIZ);\n\t\tif (n < 0) {\n\t\t\tif (errno == EINTR)\n\t\t\t\tcontinue;\n\t\t\tret = -errno;\n\t\t\tbreak;\n\t\t}\n\t\tt = n;\n\t\ts = 0;\n\t\tdo {\n\t\t\ts = write(ofd, buf+s, t);\n\t\t\tif (s < 0) {\n\t\t\t\tif (errno == EINTR)\n\t\t\t\t\tbreak;\n\t\t\t\tret = -errno;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tt -= s;\n\t\t\ts = n - t;\n\t\t} while (t);\n\t} while (n > 0 && !tracecmd_msg_done(msg_handle));\n\nout:\n\treturn ret;\n}\n\nstatic void stop_all_readers(int cpus, int *pid_array)\n{\n\tint cpu;\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\tif (pid_array[cpu] > 0)\n\t\t\tkill(pid_array[cpu], SIGUSR1);\n\t}\n}\n\nstatic int put_together_file(int cpus, int ofd, const char *node,\n\t\t\t     const char *port, bool write_options)\n{\n\tstruct tracecmd_output *handle = NULL;\n\tchar **temp_files;\n\tint cpu;\n\tint ret = -ENOMEM;\n\n\t/* Now put together the file */\n\ttemp_files = malloc(sizeof(*temp_files) * cpus);\n\tif (!temp_files)\n\t\treturn -ENOMEM;\n\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\ttemp_files[cpu] = get_temp_file(node, port, cpu);\n\t\tif (!temp_files[cpu])\n\t\t\tgoto out;\n\t}\n\n\thandle = tracecmd_get_output_handle_fd(ofd);\n\tif (!handle) {\n\t\tret = -1;\n\t\tgoto out;\n\t}\n\n\tif (write_options) {\n\t\tret = tracecmd_write_cpus(handle, cpus);\n\t\tif (ret)\n\t\t\tgoto out;\n\t\tret = tracecmd_write_buffer_info(handle);\n\t\tif (ret)\n\t\t\tgoto out;\n\t\tret = tracecmd_write_options(handle);\n\t\tif (ret)\n\t\t\tgoto out;\n\t}\n\tret = tracecmd_write_cpu_data(handle, cpus, temp_files, NULL);\n\nout:\n\ttracecmd_output_close(handle);\n\tfor (cpu--; cpu >= 0; cpu--) {\n\t\tput_temp_file(temp_files[cpu]);\n\t}\n\tfree(temp_files);\n\treturn ret;\n}\n\nstatic int process_client(struct tracecmd_msg_handle *msg_handle,\n\t\t\t  const char *node, const char *port)\n{\n\tint *pid_array;\n\tint pagesize;\n\tint cpus;\n\tint ofd;\n\tint ret;\n\n\tpagesize = communicate_with_client(msg_handle);\n\tif (pagesize < 0)\n\t\treturn pagesize;\n\n\tofd = create_client_file(node, port);\n\n\tpid_array = create_all_readers(node, port, pagesize, msg_handle);\n\tif (!pid_array) {\n\t\tclose(ofd);\n\t\treturn -ENOMEM;\n\t}\n\n\t/* on signal stop this msg */\n\tstop_msg_handle = msg_handle;\n\n\t/* Now we are ready to start reading data from the client */\n\tif (msg_handle->version == V3_PROTOCOL)\n\t\tret = tracecmd_msg_collect_data(msg_handle, ofd);\n\telse\n\t\tret = collect_metadata_from_client(msg_handle, ofd);\n\tstop_msg_handle = NULL;\n\n\t/* wait a little to let our readers finish reading */\n\tsleep(1);\n\n\tcpus = msg_handle->cpu_count;\n\n\t/* stop our readers */\n\tstop_all_readers(cpus, pid_array);\n\n\t/* wait a little to have the readers clean up */\n\tsleep(1);\n\n\tif (!ret)\n\t\tret = put_together_file(cpus, ofd, node, port,\n\t\t\t\t\tmsg_handle->version < V3_PROTOCOL);\n\n\tdestroy_all_readers(cpus, pid_array, node, port);\n\tclose(ofd);\n\n\treturn ret;\n}\n\nstatic int do_fork(int cfd)\n{\n\tpid_t pid;\n\n\t/* in debug mode, we do not fork off children */\n\tif (tracecmd_get_debug())\n\t\treturn 0;\n\n\tpid = fork();\n\tif (pid < 0) {\n\t\twarning(\"failed to create child\");\n\t\treturn -1;\n\t}\n\n\tif (pid > 0) {\n\t\tclose(cfd);\n\t\treturn pid;\n\t}\n\n\tsignal_setup(SIGINT, finish);\n\n\treturn 0;\n}\n\nbool trace_net_cmp_connection(struct sockaddr_storage *addr, const char *name)\n{\n\tchar host[NI_MAXHOST], nhost[NI_MAXHOST];\n\tchar service[NI_MAXSERV];\n\tsocklen_t addr_len = sizeof(*addr);\n\tstruct addrinfo *result, *rp;\n\tstruct addrinfo hints;\n\tbool found = false;\n\tint s;\n\n\tif (getnameinfo((struct sockaddr *)addr, addr_len,\n\t\t\thost, NI_MAXHOST,\n\t\t\tservice, NI_MAXSERV, NI_NUMERICSERV))\n\t\treturn -1;\n\n\tif (strcmp(host, name) == 0)\n\t\treturn true;\n\n\t/* Check other IPs that name could be for */\n\n\tmemset(&hints, 0, sizeof(hints));\n\thints.ai_family = AF_UNSPEC;\n\thints.ai_socktype = SOCK_STREAM;\n\n\t/* Check other IPs that name could be for */\n\ts = getaddrinfo(name, NULL, &hints, &result);\n\tif (s != 0)\n\t\treturn false;\n\n\tfor (rp = result; rp != NULL; rp = rp->ai_next) {\n\t\tif (getnameinfo(rp->ai_addr, rp->ai_addrlen,\n\t\t\t\tnhost, NI_MAXHOST,\n\t\t\t\tservice, NI_MAXSERV, NI_NUMERICSERV))\n\t\t\tcontinue;\n\t\tif (strcmp(host, nhost) == 0) {\n\t\t\tfound = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tfreeaddrinfo(result);\n\treturn found;\n}\n\nbool trace_net_cmp_connection_fd(int fd, const char *name)\n{\n\tstruct sockaddr_storage addr;\n\tsocklen_t addr_len = sizeof(addr);\n\n\tif (getpeername(fd, (struct sockaddr *)&addr, &addr_len))\n\t\treturn false;\n\n\treturn trace_net_cmp_connection(&addr, name);\n};\n\nint trace_net_print_connection(int fd)\n{\n\tchar host[NI_MAXHOST], service[NI_MAXSERV];\n\tstruct sockaddr_storage net_addr;\n\tsocklen_t addr_len;\n\n\taddr_len = sizeof(net_addr);\n\tif (getpeername(fd, (struct sockaddr *)&net_addr, &addr_len))\n\t\treturn -1;\n\n\tif (getnameinfo((struct sockaddr *)&net_addr, addr_len,\n\t\t\thost, NI_MAXHOST,\n\t\t\tservice, NI_MAXSERV, NI_NUMERICSERV))\n\t\treturn -1;\n\n\tif (tracecmd_get_debug())\n\t\ttracecmd_debug(\"Connected to %s:%s fd:%d\\n\", host, service, fd);\n\telse\n\t\ttracecmd_plog(\"Connected to %s:%s\\n\", host, service);\n\treturn 0;\n}\n\nstatic int do_connection(int cfd, struct sockaddr *addr,\n\t\t\t  socklen_t addr_len)\n{\n\tstruct tracecmd_msg_handle *msg_handle;\n\tchar host[NI_MAXHOST], service[NI_MAXSERV];\n\tint s;\n\tint ret;\n\n\tret = do_fork(cfd);\n\tif (ret)\n\t\treturn ret;\n\n\tmsg_handle = tracecmd_msg_handle_alloc(cfd, 0);\n\n\tif (use_vsock) {\n#ifdef VSOCK\n\t\tstruct sockaddr_vm *vm_addr = (struct sockaddr_vm *)addr;\n\t\tsnprintf(host, NI_MAXHOST, \"V%d\", vm_addr->svm_cid);\n\t\tsnprintf(service, NI_MAXSERV, \"%d\", vm_addr->svm_port);\n#endif\n\t} else {\n\t\ts = getnameinfo((struct sockaddr *)addr, addr_len,\n\t\t\t\thost, NI_MAXHOST,\n\t\t\t\tservice, NI_MAXSERV, NI_NUMERICSERV);\n\n\t\tif (s == 0)\n\t\t\ttracecmd_plog(\"Connected with %s:%s\\n\", host, service);\n\t\telse {\n\t\t\ttracecmd_plog(\"Error with getnameinfo: %s\\n\", gai_strerror(s));\n\t\t\tclose(cfd);\n\t\t\ttracecmd_msg_handle_close(msg_handle);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tprocess_client(msg_handle, host, service);\n\n\ttracecmd_msg_handle_close(msg_handle);\n\n\tif (!tracecmd_get_debug())\n\t\texit(0);\n\n\treturn 0;\n}\n\nstatic int *client_pids;\nstatic int free_pids;\nstatic int saved_pids;\n\nstatic void add_process(int pid)\n{\n\tint *client = NULL;\n\tint i;\n\n\tif (free_pids) {\n\t\tfor (i = 0; i < saved_pids; i++) {\n\t\t\tif (!client_pids[i]) {\n\t\t\t\tclient = &client_pids[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfree_pids--;\n\t\tif (!client)\n\t\t\twarning(\"Could not find free pid\");\n\t}\n\tif (!client) {\n\t\tclient_pids = realloc(client_pids,\n\t\t\t\t      sizeof(*client_pids) * (saved_pids + 1));\n\t\tif (!client_pids)\n\t\t\tpdie(\"allocating pids\");\n\t\tclient = &client_pids[saved_pids++];\n\t}\n\t*client = pid;\n}\n\nstatic void remove_process(int pid)\n{\n\tint i;\n\n\tfor (i = 0; i < saved_pids; i++) {\n\t\tif (client_pids[i] == pid)\n\t\t\tbreak;\n\t}\n\n\tif (i == saved_pids)\n\t\treturn;\n\n\tclient_pids[i] = 0;\n\tfree_pids++;\n}\n\nstatic void kill_clients(void)\n{\n\tint status;\n\tint i;\n\n\tfor (i = 0; i < saved_pids; i++) {\n\t\tif (!client_pids[i])\n\t\t\tcontinue;\n\t\t/* Only kill the clients if we received SIGINT or SIGTERM */\n\t\tif (done)\n\t\t\tkill(client_pids[i], SIGINT);\n\t\twaitpid(client_pids[i], &status, 0);\n\t}\n\n\tsaved_pids = 0;\n}\n\nstatic void clean_up(void)\n{\n\tint status;\n\tint ret;\n\n\t/* Clean up any children that has started before */\n\tdo {\n\t\tret = waitpid(0, &status, WNOHANG);\n\t\tif (ret > 0)\n\t\t\tremove_process(ret);\n\t} while (ret > 0);\n}\n\nstatic void do_accept_loop(int sfd)\n{\n\tstruct sockaddr_storage peer_addr;\n#ifdef VSOCK\n\tstruct sockaddr_vm vm_addr;\n#endif\n\tstruct sockaddr *addr;\n\tsocklen_t addr_len;\n\tint cfd, pid;\n\n\tif (use_vsock) {\n#ifdef VSOCK\n\t\taddr = (struct sockaddr *)&vm_addr;\n\t\taddr_len = sizeof(vm_addr);\n#endif\n\t} else {\n\t\taddr = (struct sockaddr *)&peer_addr;\n\t\taddr_len = sizeof(peer_addr);\n\t}\n\n\tdo {\n\t\tcfd = accept(sfd, addr, &addr_len);\n\t\tif (cfd < 0 && errno == EINTR) {\n\t\t\tclean_up();\n\t\t\tcontinue;\n\t\t}\n\t\tif (cfd < 0)\n\t\t\tpdie(\"connecting\");\n\n\t\tpid = do_connection(cfd, addr, addr_len);\n\t\tif (pid > 0)\n\t\t\tadd_process(pid);\n\n\t} while (!done);\n\t/* Get any final stragglers */\n\tclean_up();\n}\n\nvoid make_pid_file(const char *pidfile_basename)\n{\n\tchar buf[PATH_MAX];\n\tint fd;\n\n\tmake_pid_name(buf, pidfile_basename);\n\n\tfd = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0644);\n\tif (fd < 0) {\n\t\tperror(buf);\n\t\treturn;\n\t}\n\n\tsprintf(buf, \"%d\\n\", getpid());\n\twrite(fd, buf, strlen(buf));\n\tclose(fd);\n}\n\nstatic void sigstub(int sig)\n{\n}\n\nstatic int get_vsock(const char *port)\n{\n\tunsigned int cid;\n\tint sd;\n\n\tsd = tcmd_vsock_make(atoi(port));\n\tif (sd < 0)\n\t\treturn sd;\n\n\tcid = tcmd_vsock_local_cid();\n\tif (cid >= 0)\n\t\tprintf(\"listening on @%u:%s\\n\", cid, port);\n\n\treturn sd;\n}\n\nstatic int get_network(char *port)\n{\n\tstruct addrinfo hints;\n\tstruct addrinfo *result, *rp;\n\tint sfd, s;\n\n\tmemset(&hints, 0, sizeof(hints));\n\thints.ai_family = AF_UNSPEC;\n\thints.ai_socktype = SOCK_STREAM;\n\thints.ai_flags = AI_PASSIVE;\n\n\ts = getaddrinfo(NULL, port, &hints, &result);\n\tif (s != 0)\n\t\tpdie(\"getaddrinfo: error opening %s\", port);\n\n\tfor (rp = result; rp != NULL; rp = rp->ai_next) {\n\t\tsfd = socket(rp->ai_family, rp->ai_socktype,\n\t\t\t     rp->ai_protocol);\n\t\tif (sfd < 0)\n\t\t\tcontinue;\n\n\t\tset_tcp_no_delay(sfd, rp->ai_socktype);\n\t\tif (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)\n\t\t\tbreak;\n\n\t\tclose(sfd);\n\t}\n\n\tif (rp == NULL)\n\t\tpdie(\"Could not bind\");\n\n\tfreeaddrinfo(result);\n\n\treturn sfd;\n}\n\nstatic void do_listen(char *port)\n{\n\tint sfd;\n\n\tif (!tracecmd_get_debug())\n\t\tsignal_setup(SIGCHLD, sigstub);\n\n\tif (do_daemon)\n\t\tmake_pid_file(LISTEN_PIDFILE);\n\n\tif (use_vsock)\n\t\tsfd = get_vsock(port);\n\telse\n\t\tsfd = get_network(port);\n\n\n\tif (listen(sfd, backlog) < 0)\n\t\tpdie(\"listen\");\n\n\tdo_accept_loop(sfd);\n\n\tkill_clients();\n\n\tif (do_daemon)\n\t\tremove_pid_file(LISTEN_PIDFILE);\n}\n\nstatic void start_daemon(void)\n{\n\tdo_daemon = 1;\n\n\tif (daemon(1, 0) < 0)\n\t\tdie(\"starting daemon\");\n}\n\nenum {\n\tOPT_verbose\t= 254,\n\tOPT_debug\t= 255,\n};\n\nvoid trace_listen(int argc, char **argv)\n{\n\tchar *logfile = NULL;\n\tchar *port = NULL;\n\tint daemon = 0;\n\tint c;\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"listen\") != 0)\n\t\tusage(argv);\n\n\tfor (;;) {\n\t\tint option_index = 0;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"port\", required_argument, NULL, 'p'},\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{\"debug\", no_argument, NULL, OPT_debug},\n\t\t\t{\"verbose\", optional_argument, NULL, OPT_verbose},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long (argc-1, argv+1, \"+hp:Vo:d:l:D\",\n\t\t\tlong_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tport = optarg;\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\toutput_dir = optarg;\n\t\t\tbreak;\n\t\tcase 'V':\n\t\t\tuse_vsock = true;\n\t\t\tbreak;\n\t\tcase 'o':\n\t\t\toutput_file = optarg;\n\t\t\tbreak;\n\t\tcase 'l':\n\t\t\tlogfile = optarg;\n\t\t\tbreak;\n\t\tcase 'D':\n\t\t\tdaemon = 1;\n\t\t\tbreak;\n\t\tcase OPT_debug:\n\t\t\ttracecmd_set_debug(true);\n\t\t\tbreak;\n\t\tcase OPT_verbose:\n\t\t\tif (trace_set_verbose(optarg) < 0)\n\t\t\t\tdie(\"invalid verbose level %s\", optarg);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif (!port)\n\t\tusage(argv);\n\n\tif ((argc - optind) >= 2)\n\t\tusage(argv);\n\n\tif (!output_file)\n\t\toutput_file = default_output_file;\n\n\tif (!output_dir)\n\t\toutput_dir = default_output_dir;\n\n\tif (logfile) {\n\t\t/* set the writes to a logfile instead */\n\t\tif (tracecmd_set_logfile(logfile) < 0)\n\t\t\tdie(\"creating log file %s\", logfile);\n\t}\n\n\tif (chdir(output_dir) < 0)\n\t\tdie(\"Can't access directory %s\", output_dir);\n\n\tif (daemon)\n\t\tstart_daemon();\n\n\tsignal_setup(SIGINT, finish);\n\tsignal_setup(SIGTERM, finish);\n\n\tdo_listen(port);\n\n\treturn;\n}\n"
  },
  {
    "path": "tracecmd/trace-mem.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2013 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n *\n * This code was inspired by Ezequiel Garcia's trace_analyze program:\n *   git://github.com/ezequielgarcia/trace_analyze.git\n *\n * Unfortuntately, I hate working with Python, and I also had trouble\n * getting it to work, as I had an old python on my Fedora 13, and it\n * was written for the newer version. I decided to do some of it here\n * in C.\n */\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <signal.h>\n\n#include \"trace-local.h\"\n#include \"trace-hash-local.h\"\n#include \"list.h\"\n\nstatic int kmalloc_type;\nstatic int kmalloc_node_type;\nstatic int kfree_type;\nstatic int kmem_cache_alloc_type;\nstatic int kmem_cache_alloc_node_type;\nstatic int kmem_cache_free_type;\n\nstatic struct tep_format_field *common_type_mem;\n\nstatic struct tep_format_field *kmalloc_callsite_field;\nstatic struct tep_format_field *kmalloc_bytes_req_field;\nstatic struct tep_format_field *kmalloc_bytes_alloc_field;\nstatic struct tep_format_field *kmalloc_ptr_field;\n\nstatic struct tep_format_field *kmalloc_node_callsite_field;\nstatic struct tep_format_field *kmalloc_node_bytes_req_field;\nstatic struct tep_format_field *kmalloc_node_bytes_alloc_field;\nstatic struct tep_format_field *kmalloc_node_ptr_field;\n\nstatic struct tep_format_field *kfree_ptr_field;\n\nstatic struct tep_format_field *kmem_cache_callsite_field;\nstatic struct tep_format_field *kmem_cache_bytes_req_field;\nstatic struct tep_format_field *kmem_cache_bytes_alloc_field;\nstatic struct tep_format_field *kmem_cache_ptr_field;\n\nstatic struct tep_format_field *kmem_cache_node_callsite_field;\nstatic struct tep_format_field *kmem_cache_node_bytes_req_field;\nstatic struct tep_format_field *kmem_cache_node_bytes_alloc_field;\nstatic struct tep_format_field *kmem_cache_node_ptr_field;\n\nstatic struct tep_format_field *kmem_cache_free_ptr_field;\n\nstatic void *zalloc(size_t size)\n{\n\treturn calloc(1, size);\n}\n\nstatic struct tep_event *\nupdate_event(struct tep_handle *pevent,\n\t     const char *sys, const char *name, int *id)\n{\n\tstruct tep_event *event;\n\n\tevent = tep_find_event_by_name(pevent, sys, name);\n\tif (!event)\n\t\treturn NULL;\n\n\t*id = event->id;\n\n\treturn event;\n}\n\nstatic void update_kmalloc(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"kmem\", \"kmalloc\", &kmalloc_type);\n\tif (!event)\n\t\treturn;\n\n\tkmalloc_callsite_field = tep_find_field(event, \"call_site\");\n\tkmalloc_bytes_req_field = tep_find_field(event, \"bytes_req\");\n\tkmalloc_bytes_alloc_field = tep_find_field(event, \"bytes_alloc\");\n\tkmalloc_ptr_field = tep_find_field(event, \"ptr\");\n}\n\nstatic void update_kmalloc_node(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"kmem\", \"kmalloc_node\", &kmalloc_node_type);\n\tif (!event)\n\t\treturn;\n\n\tkmalloc_node_callsite_field = tep_find_field(event, \"call_site\");\n\tkmalloc_node_bytes_req_field = tep_find_field(event, \"bytes_req\");\n\tkmalloc_node_bytes_alloc_field = tep_find_field(event, \"bytes_alloc\");\n\tkmalloc_node_ptr_field = tep_find_field(event, \"ptr\");\n}\n\nstatic void update_kfree(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"kmem\", \"kfree\", &kfree_type);\n\tif (!event)\n\t\treturn;\n\n\tkfree_ptr_field = tep_find_field(event, \"ptr\");\n}\n\nstatic void update_kmem_cache_alloc(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"kmem\", \"kmem_cache_alloc\", &kmem_cache_alloc_type);\n\tif (!event)\n\t\treturn;\n\n\tkmem_cache_callsite_field = tep_find_field(event, \"call_site\");\n\tkmem_cache_bytes_req_field = tep_find_field(event, \"bytes_req\");\n\tkmem_cache_bytes_alloc_field = tep_find_field(event, \"bytes_alloc\");\n\tkmem_cache_ptr_field = tep_find_field(event, \"ptr\");\n}\n\nstatic void update_kmem_cache_alloc_node(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"kmem\", \"kmem_cache_alloc_node\",\n\t\t\t     &kmem_cache_alloc_node_type);\n\tif (!event)\n\t\treturn;\n\n\tkmem_cache_node_callsite_field = tep_find_field(event, \"call_site\");\n\tkmem_cache_node_bytes_req_field = tep_find_field(event, \"bytes_req\");\n\tkmem_cache_node_bytes_alloc_field = tep_find_field(event, \"bytes_alloc\");\n\tkmem_cache_node_ptr_field = tep_find_field(event, \"ptr\");\n}\n\nstatic void update_kmem_cache_free(struct tep_handle *pevent)\n{\n\tstruct tep_event *event;\n\n\tevent = update_event(pevent, \"kmem\", \"kmem_cache_free\", &kmem_cache_free_type);\n\tif (!event)\n\t\treturn;\n\n\tkmem_cache_free_ptr_field = tep_find_field(event, \"ptr\");\n}\n\nstruct func_descr {\n\tstruct func_descr\t*next;\n\tconst char\t\t*func;\n\tunsigned long\t\ttotal_alloc;\n\tunsigned long\t\ttotal_req;\n\tunsigned long\t\tcurrent_alloc;\n\tunsigned long\t\tcurrent_req;\n\tunsigned long\t\tmax_alloc;\n\tunsigned long\t\tmax_req;\n\tunsigned long\t\twaste;\n\tunsigned long\t\tmax_waste;\n};\n\nstruct ptr_descr {\n\tstruct ptr_descr\t*next;\n\tstruct func_descr\t*func;\n\tunsigned long long\tptr;\n\tunsigned long\t\talloc;\n\tunsigned long\t\treq;\n};\n\n#define HASH_BITS\t12\n#define HASH_SIZE\t(1 << HASH_BITS)\n#define HASH_MASK\t(HASH_SIZE - 1);\n\nstatic struct func_descr *func_hash[HASH_SIZE];\nstatic struct ptr_descr *ptr_hash[HASH_SIZE];\nstatic struct func_descr **func_list;\n\nstatic unsigned func_count;\n\nstatic int make_key(const void *ptr, int size)\n{\n\tint key = 0;\n\tint i;\n\tchar *kp = (char *)&key;\n\tconst char *indx = ptr;\n\n\tfor (i = 0; i < size; i++)\n\t\tkp[i & 3] ^= indx[i];\n\n\treturn trace_hash(key);\n}\n\nstatic struct func_descr *find_func(const char *func)\n{\n\tstruct func_descr *funcd;\n\tint key = make_key(func, strlen(func)) & HASH_MASK;\n\n\tfor (funcd = func_hash[key]; funcd; funcd = funcd->next) {\n\t\t/*\n\t\t * As func is always a constant to one pointer,\n\t\t * we can use a direct compare instead of strcmp.\n\t\t */\n\t\tif (funcd->func == func)\n\t\t\treturn funcd;\n\t}\n\n\treturn NULL;\n}\n\nstatic struct func_descr *create_func(const char *func)\n{\n\tstruct func_descr *funcd;\n\tint key = make_key(func, strlen(func)) & HASH_MASK;\n\n\tfuncd = zalloc(sizeof(*funcd));\n\tif (!funcd)\n\t\tdie(\"malloc\");\n\n\tfuncd->func = func;\n\tfuncd->next = func_hash[key];\n\tfunc_hash[key] = funcd;\n\n\tfunc_count++;\n\n\treturn funcd;\n}\n\nstatic struct ptr_descr *find_ptr(unsigned long long ptr)\n{\n\tstruct ptr_descr *ptrd;\n\tint key = make_key(&ptr, sizeof(ptr)) & HASH_MASK;\n\n\tfor (ptrd = ptr_hash[key]; ptrd; ptrd = ptrd->next) {\n\t\tif (ptrd->ptr == ptr)\n\t\t\treturn ptrd;\n\t}\n\n\treturn NULL;\n}\n\nstatic struct ptr_descr *create_ptr(unsigned long long ptr)\n{\n\tstruct ptr_descr *ptrd;\n\tint key = make_key(&ptr, sizeof(ptr)) & HASH_MASK;\n\n\tptrd = zalloc(sizeof(*ptrd));\n\tif (!ptrd)\n\t\tdie(\"malloc\");\n\n\tptrd->ptr = ptr;\n\tptrd->next = ptr_hash[key];\n\tptr_hash[key] = ptrd;\n\n\treturn ptrd;\n}\n\nstatic void remove_ptr(unsigned long long ptr)\n{\n\tstruct ptr_descr *ptrd, **last;\n\tint key = make_key(&ptr, sizeof(ptr)) & HASH_MASK;\n\n\tlast = &ptr_hash[key];\n\tfor (ptrd = ptr_hash[key]; ptrd; ptrd = ptrd->next) {\n\t\tif (ptrd->ptr == ptr)\n\t\t\tbreak;\n\t\tlast = &ptrd->next;\n\t}\n\n\tif (!ptrd)\n\t\treturn;\n\n\t*last = ptrd->next;\n\tfree(ptrd);\n}\n\nstatic void add_kmalloc(const char *func, unsigned long long ptr,\n\t\t\tunsigned int req, int alloc)\n{\n\tstruct func_descr *funcd;\n\tstruct ptr_descr *ptrd;\n\n\tfuncd = find_func(func);\n\tif (!funcd)\n\t\tfuncd = create_func(func);\n\n\tfuncd->total_alloc += alloc;\n\tfuncd->total_req += req;\n\tfuncd->current_alloc += alloc;\n\tfuncd->current_req += req;\n\tif (funcd->current_alloc > funcd->max_alloc)\n\t\tfuncd->max_alloc = funcd->current_alloc;\n\tif (funcd->current_req > funcd->max_req)\n\t\tfuncd->max_req = funcd->current_req;\n\n\tptrd = find_ptr(ptr);\n\tif (!ptrd)\n\t\tptrd = create_ptr(ptr);\n\n\tptrd->alloc = alloc;\n\tptrd->req = req;\n\tptrd->func = funcd;\n}\n\nstatic void remove_kmalloc(unsigned long long ptr)\n{\n\tstruct func_descr *funcd;\n\tstruct ptr_descr *ptrd;\n\n\tptrd = find_ptr(ptr);\n\tif (!ptrd)\n\t\treturn;\n\n\tfuncd = ptrd->func;\n\tfuncd->current_alloc -= ptrd->alloc;\n\tfuncd->current_req -= ptrd->req;\n\n\tremove_ptr(ptr);\n}\n\nstatic void\nprocess_kmalloc(struct tep_handle *pevent, struct tep_record *record,\n\t\tstruct tep_format_field *callsite_field,\n\t\tstruct tep_format_field *bytes_req_field,\n\t\tstruct tep_format_field *bytes_alloc_field,\n\t\tstruct tep_format_field *ptr_field)\n{\n\tunsigned long long callsite;\n\tunsigned long long val;\n\tunsigned long long ptr;\n\tunsigned int req;\n\tint alloc;\n\tconst char *func;\n\n\ttep_read_number_field(callsite_field, record->data, &callsite);\n\ttep_read_number_field(bytes_req_field, record->data, &val);\n\treq = val;\n\ttep_read_number_field(bytes_alloc_field, record->data, &val);\n\talloc = val;\n\ttep_read_number_field(ptr_field, record->data, &ptr);\n\n\tfunc = tep_find_function(pevent, callsite);\n\n\tadd_kmalloc(func, ptr, req, alloc);\n}\n\nstatic void\nprocess_kfree(struct tep_handle *pevent, struct tep_record *record,\n\t      struct tep_format_field *ptr_field)\n{\n\tunsigned long long ptr;\n\n\ttep_read_number_field(ptr_field, record->data, &ptr);\n\n\tremove_kmalloc(ptr);\n}\n\nstatic void\nprocess_record(struct tep_handle *pevent, struct tep_record *record)\n{\n\tunsigned long long val;\n\tint type;\n\n\ttep_read_number_field(common_type_mem, record->data, &val);\n\ttype = val;\n\n\tif (type == kmalloc_type)\n\t\treturn process_kmalloc(pevent, record,\n\t\t\t\t       kmalloc_callsite_field,\n\t\t\t\t       kmalloc_bytes_req_field,\n\t\t\t\t       kmalloc_bytes_alloc_field,\n\t\t\t\t       kmalloc_ptr_field);\n\tif (type == kmalloc_node_type)\n\t\treturn process_kmalloc(pevent, record,\n\t\t\t\t       kmalloc_node_callsite_field,\n\t\t\t\t       kmalloc_node_bytes_req_field,\n\t\t\t\t       kmalloc_node_bytes_alloc_field,\n\t\t\t\t       kmalloc_node_ptr_field);\n\tif (type == kfree_type)\n\t\treturn process_kfree(pevent, record, kfree_ptr_field);\n\n\tif (type == kmem_cache_alloc_type)\n\t\treturn process_kmalloc(pevent, record,\n\t\t\t\t       kmem_cache_callsite_field,\n\t\t\t\t       kmem_cache_bytes_req_field,\n\t\t\t\t       kmem_cache_bytes_alloc_field,\n\t\t\t\t       kmem_cache_ptr_field);\n\tif (type == kmem_cache_alloc_node_type)\n\t\treturn process_kmalloc(pevent, record,\n\t\t\t\t       kmem_cache_node_callsite_field,\n\t\t\t\t       kmem_cache_node_bytes_req_field,\n\t\t\t\t       kmem_cache_node_bytes_alloc_field,\n\t\t\t\t       kmem_cache_node_ptr_field);\n\tif (type == kmem_cache_free_type)\n\t\treturn process_kfree(pevent, record, kmem_cache_free_ptr_field);\n}\n\nstatic int func_cmp(const void *a, const void *b)\n{\n\tconst struct func_descr *fa = *(const struct func_descr **)a;\n\tconst struct func_descr *fb = *(const struct func_descr **)b;\n\n\tif (fa->waste > fb->waste)\n\t\treturn -1;\n\tif (fa->waste < fb->waste)\n\t\treturn 1;\n\treturn 0;\n}\n\nstatic void sort_list(void)\n{\n\tstruct func_descr *funcd;\n\tint h;\n\tint i = 0;\n\n\tfunc_list = zalloc(sizeof(*func_list) * func_count);\n\n\tfor (h = 0; h < HASH_SIZE; h++) {\n\t\tfor (funcd = func_hash[h]; funcd; funcd = funcd->next) {\n\t\t\tfuncd->waste = funcd->current_alloc - funcd->current_req;\n\t\t\tfuncd->max_waste = funcd->max_alloc - funcd->max_req;\n\t\t\tif (i == func_count)\n\t\t\t\tdie(\"more funcs than expected\\n\");\n\t\t\tfunc_list[i++] = funcd;\n\t\t}\n\t}\n\n\tqsort(func_list, func_count, sizeof(*func_list), func_cmp);\n}\n\nstatic void print_list(void)\n{\n\tstruct func_descr *funcd;\n\tint i;\n\n\tprintf(\"                Function            \\t\");\n\tprintf(\"Waste\\tAlloc\\treq\\t\\tTotAlloc     TotReq\\t\\tMaxAlloc     MaxReq\\t\");\n\tprintf(\"MaxWaste\\n\");\n\tprintf(\"                --------            \\t\");\n\tprintf(\"-----\\t-----\\t---\\t\\t--------     ------\\t\\t--------     ------\\t\");\n\tprintf(\"--------\\n\");\n\t\n\tfor (i = 0; i < func_count; i++) {\n\t\tfuncd = func_list[i];\n\n\t\tprintf(\"%32s\\t%ld\\t%ld\\t%ld\\t\\t%8ld   %8ld\\t\\t%8ld   %8ld\\t%ld\\n\",\n\t\t       funcd->func, funcd->waste,\n\t\t       funcd->current_alloc, funcd->current_req,\n\t\t       funcd->total_alloc, funcd->total_req,\n\t\t       funcd->max_alloc, funcd->max_req, funcd->max_waste);\n\t}\n}\n\nstatic void do_trace_mem(struct tracecmd_input *handle)\n{\n\tstruct tep_handle *pevent = tracecmd_get_tep(handle);\n\tstruct tep_record *record;\n\tstruct tep_event *event;\n\tint missed_events = 0;\n\tint cpus;\n\tint cpu;\n\tint ret;\n\n\tret = tracecmd_init_data(handle);\n\tif (ret < 0)\n\t\tdie(\"failed to init data\");\n\n\tif (ret > 0)\n\t\tdie(\"trace-cmd mem does not work with latency traces\\n\");\n\n\tcpus = tracecmd_cpus(handle);\n\n\t/* Need to get any event */\n\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\trecord = tracecmd_peek_data(handle, cpu);\n\t\tif (record)\n\t\t\tbreak;\n\t}\n\tif (!record)\n\t\tdie(\"No records found in file\");\n\n\tret = tep_data_type(pevent, record);\n\tevent = tep_find_event(pevent, ret);\n\n\tcommon_type_mem = tep_find_common_field(event, \"common_type\");\n\tif (!common_type_mem)\n\t\tdie(\"Can't find a 'type' field?\");\n\n\tupdate_kmalloc(pevent);\n\tupdate_kmalloc_node(pevent);\n\tupdate_kfree(pevent);\n\tupdate_kmem_cache_alloc(pevent);\n\tupdate_kmem_cache_alloc_node(pevent);\n\tupdate_kmem_cache_free(pevent);\n\n\twhile ((record = tracecmd_read_next_data(handle, &cpu))) {\n\n\t\t/* record missed event */\n\t\tif (!missed_events && record->missed_events)\n\t\t\tmissed_events = 1;\n\n\t\tprocess_record(pevent, record);\n\t\ttracecmd_free_record(record);\n\t}\n\n\tsort_list();\n\tprint_list();\n}\n\nvoid trace_mem(int argc, char **argv)\n{\n\tstruct tracecmd_input *handle;\n\tconst char *input_file = NULL;\n\tint ret;\n\n\tfor (;;) {\n\t\tint c;\n\n\t\tc = getopt(argc-1, argv+1, \"+hi:\");\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\tif (input_file)\n\t\t\t\tdie(\"Only one input for mem\");\n\t\t\tinput_file = optarg;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif ((argc - optind) >= 2) {\n\t\tif (input_file)\n\t\t\tusage(argv);\n\t\tinput_file = argv[optind + 1];\n\t}\n\n\tif (!input_file)\n\t\tinput_file = DEFAULT_INPUT_FILE;\n\n\thandle = tracecmd_alloc(input_file, 0);\n\tif (!handle)\n\t\tdie(\"can't open %s\\n\", input_file);\n\n\tret = tracecmd_read_headers(handle, 0);\n\tif (!ret)\n\t\tdo_trace_mem(handle);\n\n\ttracecmd_close(handle);\n}\n"
  },
  {
    "path": "tracecmd/trace-profile.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n\n/** FIXME: Convert numbers based on machine and file */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef NO_AUDIT\n#include <libaudit.h>\n#endif\n#include \"trace-local.h\"\n#include \"trace-hash.h\"\n#include \"trace-hash-local.h\"\n#include \"list.h\"\n\n#include <linux/time64.h>\n\n#ifdef WARN_NO_AUDIT\n# warning \"lib audit not found, using raw syscalls \"\t\\\n\t\"(install audit-libs-devel(for fedora) or libaudit-dev(for debian/ubuntu) and try again)\"\n#endif\n\n#define TASK_STATE_TO_CHAR_STR \"RSDTtXZxKWP\"\n#define TASK_STATE_MAX\t\t1024\n\n#define task_from_item(item)\tcontainer_of(item, struct task_data, hash)\n#define start_from_item(item)\tcontainer_of(item, struct start_data, hash)\n#define event_from_item(item)\tcontainer_of(item, struct event_hash, hash)\n#define stack_from_item(item)\tcontainer_of(item, struct stack_data, hash)\n#define group_from_item(item)\tcontainer_of(item, struct group_data, hash)\n#define event_data_from_item(item)\tcontainer_of(item, struct event_data, hash)\n\nstatic unsigned long long nsecs_per_sec(unsigned long long ts)\n{\n\treturn ts / NSEC_PER_SEC;\n}\n\nstatic unsigned long long mod_to_usec(unsigned long long ts)\n{\n\treturn ((ts % NSEC_PER_SEC) + NSEC_PER_USEC / 2) / NSEC_PER_USEC;\n}\n\nstruct handle_data;\nstruct event_hash;\nstruct event_data;\n\ntypedef void (*event_data_print)(struct trace_seq *s, struct event_hash *hash);\ntypedef int (*handle_event_func)(struct handle_data *h, unsigned long long pid,\n\t\t\t\t struct event_data *data,\n\t\t\t\t struct tep_record *record, int cpu);\n\nenum event_data_type {\n\tEVENT_TYPE_UNDEFINED,\n\tEVENT_TYPE_STACK,\n\tEVENT_TYPE_SCHED_SWITCH,\n\tEVENT_TYPE_WAKEUP,\n\tEVENT_TYPE_FUNC,\n\tEVENT_TYPE_SYSCALL,\n\tEVENT_TYPE_IRQ,\n\tEVENT_TYPE_SOFTIRQ,\n\tEVENT_TYPE_SOFTIRQ_RAISE,\n\tEVENT_TYPE_PROCESS_EXEC,\n\tEVENT_TYPE_USER_MATE,\n};\n\nstruct event_data {\n\tstruct trace_hash_item\thash;\n\tint\t\t\tid;\n\tint\t\t\ttrace;\n\tstruct tep_event\t*event;\n\n\tstruct event_data\t*end;\n\tstruct event_data\t*start;\n\n\tstruct tep_format_field\t*pid_field;\n\tstruct tep_format_field\t*start_match_field;\t/* match with start */\n\tstruct tep_format_field\t*end_match_field;\t/* match with end */\n\tstruct tep_format_field\t*data_field;\t/* optional */\n\n\tevent_data_print\tprint_func;\n\thandle_event_func\thandle_event;\n\tvoid\t\t\t*private;\n\tint\t\t\tmigrate;\t/* start/end pairs can migrate cpus */\n\tint\t\t\tglobal;\t\t/* use global tasks */\n\tenum event_data_type\ttype;\n};\n\nstruct stack_data {\n\tstruct trace_hash_item  hash;\n\tunsigned long long\tcount;\n\tunsigned long long\ttime;\n\tunsigned long long\ttime_min;\n\tunsigned long long\tts_min;\n\tunsigned long long\ttime_max;\n\tunsigned long long\tts_max;\n\tunsigned long long\ttime_avg;\n\tunsigned long\t\tsize;\n\tchar\t\t\tcaller[];\n};\n\nstruct stack_holder {\n\tunsigned long\t\tsize;\n\tvoid\t\t\t*caller;\n\tstruct tep_record\t*record;\n};\n\nstruct start_data {\n\tstruct trace_hash_item\thash;\n\tstruct event_data\t*event_data;\n\tstruct list_head\tlist;\n\tstruct task_data\t*task;\n\tunsigned long long \ttimestamp;\n\tunsigned long long \tsearch_val;\n\tunsigned long long\tval;\n\tint\t\t\tcpu;\n\n\tstruct stack_holder\tstack;\n};\n\nstruct event_hash {\n\tstruct trace_hash_item\thash;\n\tstruct event_data\t*event_data;\n\tunsigned long long\tsearch_val;\n\tunsigned long long\tval;\n\tunsigned long long\tcount;\n\tunsigned long long\ttime_total;\n\tunsigned long long\ttime_avg;\n\tunsigned long long\ttime_max;\n\tunsigned long long\tts_max;\n\tunsigned long long\ttime_min;\n\tunsigned long long\tts_min;\n\tunsigned long long\ttime_std;\n\tunsigned long long\tlast_time;\n\n\tstruct trace_hash\tstacks;\n};\n\nstruct group_data {\n\tstruct trace_hash_item\thash;\n\tchar\t\t\t*comm;\n\tstruct trace_hash\tevent_hash;\n};\n\nstruct task_data {\n\tstruct trace_hash_item\thash;\n\tint\t\t\tpid;\n\tint\t\t\tsleeping;\n\n\tchar\t\t\t*comm;\n\n\tstruct trace_hash\tstart_hash;\n\tstruct trace_hash\tevent_hash;\n\n\tstruct task_data\t*proxy;\n\tstruct start_data\t*last_start;\n\tstruct event_hash\t*last_event;\n\tstruct tep_record\t*last_stack;\n\tstruct handle_data\t*handle;\n\tstruct group_data\t*group;\n};\n\nstruct cpu_info {\n\tint\t\t\tcurrent;\n};\n\nstruct sched_switch_data {\n\tstruct tep_format_field\t*prev_state;\n\tint\t\t\tmatch_state;\n};\n\nstruct handle_data {\n\tstruct handle_data\t*next;\n\tstruct tracecmd_input\t*handle;\n\tstruct tep_handle\t*pevent;\n\n\tstruct trace_hash\tevents;\n\tstruct trace_hash\tgroup_hash;\n\n\tstruct cpu_info\t\t**cpu_data;\n\n\tstruct tep_format_field\t*common_pid;\n\tstruct tep_format_field\t*wakeup_comm;\n\tstruct tep_format_field\t*switch_prev_comm;\n\tstruct tep_format_field\t*switch_next_comm;\n\n\tstruct sched_switch_data sched_switch_blocked;\n\tstruct sched_switch_data sched_switch_preempt;\n\n\tstruct trace_hash\ttask_hash;\n\tstruct list_head\t*cpu_starts;\n\tstruct list_head\tmigrate_starts;\n\n\tstruct task_data\t*global_task;\n\tstruct task_data\t*global_percpu_tasks;\n\n\tint\t\t\tcpus;\n};\n\nstatic struct handle_data *handles;\nstatic struct event_data *stacktrace_event;\nstatic bool merge_like_comms = false;\n\nvoid trace_profile_set_merge_like_comms(void)\n{\n\tmerge_like_comms = true;\n}\n\nstatic struct start_data *\nadd_start(struct task_data *task,\n\t  struct event_data *event_data, struct tep_record *record,\n\t  unsigned long long search_val, unsigned long long val)\n{\n\tstruct start_data *start;\n\n\tstart = malloc(sizeof(*start));\n\tif (!start)\n\t\treturn NULL;\n\tmemset(start, 0, sizeof(*start));\n\tstart->hash.key = trace_hash(search_val);\n\tstart->search_val = search_val;\n\tstart->val = val;\n\tstart->timestamp = record->ts;\n\tstart->event_data = event_data;\n\tstart->cpu = record->cpu;\n\tstart->task = task;\n\ttcmd_hash_add(&task->start_hash, &start->hash);\n\tif (event_data->migrate)\n\t\tlist_add(&start->list, &task->handle->migrate_starts);\n\telse\n\t\tlist_add(&start->list, &task->handle->cpu_starts[record->cpu]);\n\treturn start;\n}\n\nstruct event_data_match {\n\tstruct event_data\t*event_data;\n\tunsigned long long\tsearch_val;\n\tunsigned long long\tval;\n};\n\nstatic int match_start(struct trace_hash_item *item, void *data)\n{\n\tstruct start_data *start = start_from_item(item);\n\tstruct event_data_match *edata = data;\n\n\treturn start->event_data == edata->event_data &&\n\t\tstart->search_val == edata->search_val;\n}\n\nstatic int match_event(struct trace_hash_item *item, void *data)\n{\n\tstruct event_data_match *edata = data;\n\tstruct event_hash *event = event_from_item(item);\n\n\treturn event->event_data == edata->event_data &&\n\t\tevent->search_val == edata->search_val &&\n\t\tevent->val == edata->val;\n}\n\nstatic struct event_hash *\nfind_event_hash(struct task_data *task, struct event_data_match *edata)\n{\n\tstruct event_hash *event_hash;\n\tstruct trace_hash_item *item;\n\tunsigned long long key;\n\n\tkey = (unsigned long)edata->event_data +\n\t\t(unsigned long)edata->search_val +\n\t\t(unsigned long)edata->val;\n\tkey = trace_hash(key);\n\titem = tcmd_hash_find(&task->event_hash, key, match_event, edata);\n\tif (item)\n\t\treturn event_from_item(item);\n\n\tevent_hash = malloc(sizeof(*event_hash));\n\tif (!event_hash)\n\t\treturn NULL;\n\tmemset(event_hash, 0, sizeof(*event_hash));\n\n\tevent_hash->event_data = edata->event_data;\n\tevent_hash->search_val = edata->search_val;\n\tevent_hash->val = edata->val;\n\tevent_hash->hash.key = key;\n\ttcmd_hash_init(&event_hash->stacks, 32);\n\n\ttcmd_hash_add(&task->event_hash, &event_hash->hash);\n\n\treturn event_hash;\n}\n\nstatic struct event_hash *\nfind_start_event_hash(struct task_data *task, struct event_data *event_data,\n\t\t      struct start_data *start)\n{\n\tstruct event_data_match edata;\n\n\tedata.event_data = event_data;\n\tedata.search_val = start->search_val;\n\tedata.val = start->val;\n\n\treturn find_event_hash(task, &edata);\n}\n\nstatic struct start_data *\nfind_start(struct task_data *task, struct event_data *event_data,\n\t   unsigned long long search_val)\n{\n\tunsigned long long key = trace_hash(search_val);\n\tstruct event_data_match edata;\n\tvoid *data = &edata;\n\tstruct trace_hash_item *item;\n\tstruct start_data *start;\n\n\tedata.event_data = event_data;\n\tedata.search_val = search_val;\n\n\titem = tcmd_hash_find(&task->start_hash, key, match_start, data);\n\tif (!item)\n\t\treturn NULL;\n\n\tstart = start_from_item(item);\n\treturn start;\n}\n\nstruct stack_match {\n\tvoid\t\t*caller;\n\tunsigned long\tsize;\n};\n\nstatic int match_stack(struct trace_hash_item *item, void *data)\n{\n\tstruct stack_data *stack = stack_from_item(item);\n\tstruct stack_match *match = data;\n\n\tif (match->size != stack->size)\n\t\treturn 0;\n\n\treturn memcmp(stack->caller, match->caller, stack->size) == 0;\n}\n\n\nstatic void add_event_stack(struct event_hash *event_hash,\n\t\t\t    void *caller, unsigned long size,\n\t\t\t    unsigned long long time, unsigned long long ts)\n{\n\tunsigned long long key;\n\tstruct stack_data *stack;\n\tstruct stack_match match;\n\tstruct trace_hash_item *item;\n\tint i;\n\n\tmatch.caller = caller;\n\tmatch.size = size;\n\n\tif (size < sizeof(int))\n\t\tdie(\"Stack size of less than sizeof(int)??\");\n\n\tfor (key = 0, i = 0; i <= size - sizeof(int); i += sizeof(int))\n\t\tkey += trace_hash(*(int *)(caller + i));\n\n\titem = tcmd_hash_find(&event_hash->stacks, key, match_stack, &match);\n\tif (!item) {\n\t\tstack = malloc(sizeof(*stack) + size);\n\t\tif (!stack) {\n\t\t\twarning(\"Could not allocate stack\");\n\t\t\treturn;\n\t\t}\n\t\tmemset(stack, 0, sizeof(*stack));\n\t\tmemcpy(&stack->caller, caller, size);\n\t\tstack->size = size;\n\t\tstack->hash.key = key;\n\t\ttcmd_hash_add(&event_hash->stacks, &stack->hash);\n\t} else\n\t\tstack = stack_from_item(item);\n\n\tstack->count++;\n\tstack->time += time;\n\tif (stack->count == 1 || time < stack->time_min) {\n\t\tstack->time_min = time;\n\t\tstack->ts_min = ts;\n\t}\n\tif (time > stack->time_max) {\n\t\tstack->time_max = time;\n\t\tstack->ts_max = ts;\n\t}\n}\n\nstatic void free_start(struct start_data *start)\n{\n\tif (start->task->last_start == start)\n\t\tstart->task->last_start = NULL;\n\tif (start->stack.record)\n\t\ttracecmd_free_record(start->stack.record);\n\ttrace_hash_del(&start->hash);\n\tlist_del(&start->list);\n\tfree(start);\n}\n\nstatic struct event_hash *\nadd_and_free_start(struct task_data *task, struct start_data *start,\n\t\t   struct event_data *event_data, unsigned long long ts)\n{\n\tstruct event_hash *event_hash;\n\tlong long delta;\n\n\tdelta = ts - start->timestamp;\n\n\t/*\n\t * It's possible on a live trace, because of timestamps being\n\t * different on different CPUs, we can go back in time. When\n\t * that happens, just zero out the delta.\n\t */\n\tif (delta < 0)\n\t\tdelta = 0;\n\n\tevent_hash = find_start_event_hash(task, event_data, start);\n\tif (!event_hash)\n\t\treturn NULL;\n\tevent_hash->count++;\n\tevent_hash->time_total += delta;\n\tevent_hash->last_time = delta;\n\n\tif (delta > event_hash->time_max) {\n\t\tevent_hash->time_max = delta;\n\t\tevent_hash->ts_max = ts;\n\t}\n\n\tif (event_hash->count == 1 || delta < event_hash->time_min) {\n\t\tevent_hash->time_min = delta;\n\t\tevent_hash->ts_min = ts;\n\t}\n\n\tif (start->stack.record) {\n\t\tunsigned long size;\n\t\tvoid *caller;\n\n\t\tsize = start->stack.size;\n\t\tcaller = start->stack.caller;\n\n\t\tadd_event_stack(event_hash, caller, size, delta,\n\t\t\t\tstart->stack.record->ts);\n\t\ttracecmd_free_record(start->stack.record);\n\t\tstart->stack.record = NULL;\n\t}\n\n\tfree_start(start);\n\n\treturn event_hash;\n}\n\nstatic struct event_hash *\nfind_and_update_start(struct task_data *task, struct event_data *event_data,\n\t\t      unsigned long long ts, unsigned long long search_val)\n{\n\tstruct start_data *start;\n\n\tstart = find_start(task, event_data, search_val);\n\tif (!start)\n\t\treturn NULL;\n\treturn add_and_free_start(task, start, event_data, ts);\n}\n\nstatic int match_task(struct trace_hash_item *item, void *data)\n{\n\tstruct task_data *task = task_from_item(item);\n\tint pid = *(unsigned long *)data;\n\n\treturn task->pid == pid;\n}\n\nstatic void init_task(struct handle_data *h, struct task_data *task)\n{\n\ttask->handle = h;\n\n\ttcmd_hash_init(&task->start_hash, 16);\n\ttcmd_hash_init(&task->event_hash, 32);\n}\n\nstatic struct task_data *\nadd_task(struct handle_data *h, int pid)\n{\n\tunsigned long long key = trace_hash(pid);\n\tstruct task_data *task;\n\n\ttask = malloc(sizeof(*task));\n\tif (!task) {\n\t\twarning(\"Could not allocate task\");\n\t\treturn NULL;\n\t}\n\tmemset(task, 0, sizeof(*task));\n\n\ttask->pid = pid;\n\ttask->hash.key = key;\n\ttcmd_hash_add(&h->task_hash, &task->hash);\n\n\tinit_task(h, task);\n\n\treturn task;\n}\n\nstatic struct task_data *\nfind_task(struct handle_data *h, int pid)\n{\n\tunsigned long long key = trace_hash(pid);\n\tstruct trace_hash_item *item;\n\tstatic struct task_data *last_task;\n\tvoid *data = (unsigned long *)&pid;\n\n\tif (last_task && last_task->pid == pid)\n\t\treturn last_task;\n\n\titem = tcmd_hash_find(&h->task_hash, key, match_task, data);\n\n\tif (item)\n\t\tlast_task = task_from_item(item);\n\telse\n\t\tlast_task = add_task(h, pid);\n\n\treturn last_task;\n}\n\nstatic int match_group(struct trace_hash_item *item, void *data)\n{\n\tstruct group_data *group = group_from_item(item);\n\n\treturn strcmp(group->comm, (char *)data) == 0;\n}\n\n\nstatic void\nadd_task_comm(struct task_data *task, struct tep_format_field *field,\n\t      struct tep_record *record)\n{\n\tconst char *comm;\n\n\ttask->comm = malloc(field->size + 1);\n\tif (!task->comm) {\n\t\twarning(\"Could not allocate task comm\");\n\t\treturn;\n\t}\n\tcomm = record->data + field->offset;\n\tmemcpy(task->comm, comm, field->size);\n\ttask->comm[field->size] = 0;\n}\n\n/* Account for tasks that don't have starts */\nstatic void account_task(struct task_data *task, struct event_data *event_data,\n\t\t\t struct tep_record *record)\n{\n\tstruct event_data_match edata;\n\tstruct event_hash *event_hash;\n\tstruct task_data *proxy = NULL;\n\tunsigned long long search_val = 0;\n\tunsigned long long val = 0;\n\tunsigned long long pid;\n\n\t/*\n\t * If an event has the pid_field set, then find that task for\n\t * this event instead. Let this task proxy for it to handle\n\t * stack traces on this event.\n\t */\n\tif (event_data->pid_field) {\n\t\ttep_read_number_field(event_data->pid_field,\n\t\t\t\t      record->data, &pid);\n\t\tproxy = task;\n\t\ttask = find_task(task->handle, pid);\n\t\tif (!task)\n\t\t\treturn;\n\t\tproxy->proxy = task;\n\t}\n\n\t/*\n\t * If data_field is defined, use that for val,\n\t * if the start_field is defined, use that for search_val.\n\t */\n\tif (event_data->data_field) {\n\t\ttep_read_number_field(event_data->data_field,\n\t\t\t\t      record->data, &val);\n\t}\n\tif (event_data->start_match_field) {\n\t\ttep_read_number_field(event_data->start_match_field,\n\t\t\t\t      record->data, &search_val);\n\t}\n\n\tedata.event_data = event_data;\n\tedata.search_val = val;\n\tedata.val = val;\n\n\tevent_hash = find_event_hash(task, &edata);\n\tif (!event_hash) {\n\t\twarning(\"failed to allocate event_hash\");\n\t\treturn;\n\t}\n\n\tevent_hash->count++;\n\ttask->last_event = event_hash;\n}\n\nstatic struct task_data *\nfind_event_task(struct handle_data *h, struct event_data *event_data,\n\t\tstruct tep_record *record, unsigned long long pid)\n{\n\tif (event_data->global) {\n\t\tif (event_data->migrate)\n\t\t\treturn h->global_task;\n\t\telse\n\t\t\treturn &h->global_percpu_tasks[record->cpu];\n\t}\n\n\t/* If pid_field is defined, use that to find the task */\n\tif (event_data->pid_field)\n\t\ttep_read_number_field(event_data->pid_field,\n\t\t\t\t      record->data, &pid);\n\treturn find_task(h, pid);\n}\n\nstatic struct task_data *\nhandle_end_event(struct handle_data *h, struct event_data *event_data,\n\t\t struct tep_record *record, int pid)\n{\n\tstruct event_hash *event_hash;\n\tstruct task_data *task;\n\tunsigned long long val;\n\n\ttask = find_event_task(h, event_data, record, pid);\n\tif (!task)\n\t\treturn NULL;\n\n\ttep_read_number_field(event_data->start_match_field, record->data,\n\t\t\t      &val);\n\tevent_hash = find_and_update_start(task, event_data->start, record->ts, val);\n\ttask->last_start = NULL;\n\ttask->last_event = event_hash;\n\n\treturn task;\n}\n\nstatic struct task_data *\nhandle_start_event(struct handle_data *h, struct event_data *event_data,\n\t\t   struct tep_record *record, unsigned long long pid)\n{\n\tstruct start_data *start;\n\tstruct task_data *task;\n\tunsigned long long val;\n\n\ttask = find_event_task(h, event_data, record, pid);\n\tif (!task)\n\t\treturn NULL;\n\n\ttep_read_number_field(event_data->end_match_field, record->data,\n\t\t\t\t &val);\n\tstart = add_start(task, event_data, record, val, val);\n\tif (!start) {\n\t\twarning(\"Failed to allocate start of task\");\n\t\treturn NULL;\n\t}\n\t\t\n\ttask->last_start = start;\n\ttask->last_event = NULL;\n\n\treturn task;\n}\n\nstatic int handle_event_data(struct handle_data *h,\n\t\t\t     unsigned long long pid,\n\t\t\t     struct event_data *event_data,\n\t\t\t     struct tep_record *record, int cpu)\n{\n\tstruct task_data *task = NULL;\n\n\t/* If this is the end of a event pair (start is set) */\n\tif (event_data->start)\n\t\ttask = handle_end_event(h, event_data, record, pid);\n\n\t/* If this is the start of a event pair (end is set) */\n\tif (event_data->end) {\n\t\ttask = handle_start_event(h, event_data, record, pid);\n\t\t/* handle_start_event only returns NULL on error */\n\t\tif (!task)\n\t\t\treturn -1;\n\t}\n\n\tif (!task) {\n\t\ttask = find_task(h, pid);\n\t\tif (!task)\n\t\t\treturn -1;\n\t\ttask->proxy = NULL;\n\t\ttask->last_start = NULL;\n\t\ttask->last_event = NULL;\n\t\taccount_task(task, event_data, record);\n\t}\n\n\treturn 0;\n}\n\nstatic void handle_missed_events(struct handle_data *h, int cpu)\n{\n\tstruct start_data *start;\n\tstruct start_data *n;\n\n\t/* Clear all starts on this CPU */\n\tlist_for_each_entry_safe(start, n, &h->cpu_starts[cpu], list) {\n\t\tfree_start(start);\n\t}\n\n\t/* Now clear all starts whose events can migrate */\n\tlist_for_each_entry_safe(start, n, &h->migrate_starts, list) {\n\t\tfree_start(start);\n\t}\n}\n\nstatic int match_event_data(struct trace_hash_item *item, void *data)\n{\n\tstruct event_data *event_data = event_data_from_item(item);\n\tint id = (int)(unsigned long)data;\n\n\treturn event_data->id == id;\n}\n\nstatic struct event_data *\nfind_event_data(struct handle_data *h, int id)\n{\n\tstruct trace_hash_item *item;\n\tunsigned long long key = trace_hash(id);\n\tvoid *data = (void *)(unsigned long)id;\n\n\titem = tcmd_hash_find(&h->events, key, match_event_data, data);\n\tif (item)\n\t\treturn event_data_from_item(item);\n\treturn NULL;\n}\n\nstatic void trace_profile_record(struct tracecmd_input *handle,\n\t\t\t\t struct tep_record *record)\n{\n\tstatic struct handle_data *last_handle;\n\tstruct tep_record *stack_record;\n\tstruct event_data *event_data;\n\tstruct task_data *task;\n\tstruct handle_data *h;\n\tstruct tep_handle *pevent;\n\tunsigned long long pid;\n\tint cpu = record->cpu;\n\tint id;\n\n\tif (last_handle && last_handle->handle == handle)\n\t\th = last_handle;\n\telse {\n\t\tfor (h = handles; h; h = h->next) {\n\t\t\tif (h->handle == handle)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!h)\n\t\t\tdie(\"Handle not found?\");\n\t\tlast_handle = h;\n\t}\n\n\tif (record->missed_events)\n\t\thandle_missed_events(h, cpu);\n\n\tpevent = h->pevent;\n\n\tid = tep_data_type(pevent, record);\n\n\tevent_data = find_event_data(h, id);\n\n\tif (!event_data)\n\t\treturn;\n\n\n\t/* Get this current PID */\n\ttep_read_number_field(h->common_pid, record->data, &pid);\n\n\ttask = find_task(h, pid);\n\tif (!task)\n\t\treturn;\n\tstack_record = task->last_stack;\n\n\tif (event_data->handle_event)\n\t\tevent_data->handle_event(h, pid, event_data, record, cpu);\n\telse\n\t\thandle_event_data(h, pid, event_data, record, cpu);\n\n\t/* If the last stack hasn't changed, free it */\n\tif (stack_record && task->last_stack == stack_record) {\n\t\ttracecmd_free_record(stack_record);\n\t\ttask->last_stack = NULL;\n\t}\n}\n\nstatic struct event_data *\nadd_event(struct handle_data *h, const char *system, const char *event_name,\n\t  enum event_data_type type)\n{\n\tstruct event_data *event_data;\n\tstruct tep_event *event;\n\n\tevent = tep_find_event_by_name(h->pevent, system, event_name);\n\tif (!event)\n\t\treturn NULL;\n\n\tif (!h->common_pid) {\n\t\th->common_pid = tep_find_common_field(event, \"common_pid\");\n\t\tif (!h->common_pid)\n\t\t\tdie(\"No 'common_pid' found in event\");\n\t}\n\n\tevent_data = malloc(sizeof(*event_data));\n\tif (!event_data) {\n\t\twarning(\"Could not allocate event_data\");\n\t\treturn NULL;\n\t}\n\tmemset(event_data, 0, sizeof(*event_data));\n\tevent_data->id = event->id;\n\tevent_data->event = event;\n\tevent_data->type = type;\n\tevent_data->hash.key = trace_hash(event_data->event->id);\n\n\ttcmd_hash_add(&h->events, &event_data->hash);\n\n\treturn event_data;\n}\n\nstatic void\nmate_events(struct handle_data *h, struct event_data *start,\n\t    const char *pid_field, const char *end_match_field,\n\t    struct event_data *end, const char *start_match_field,\n\t    int migrate, int global)\n{\n\tstart->end = end;\n\tend->start = start;\n\n\tif (pid_field) {\n\t\tstart->pid_field = tep_find_field(start->event, pid_field);\n\t\tif (!start->pid_field)\n\t\t\tdie(\"Event: %s does not have field %s\",\n\t\t\t    start->event->name, pid_field);\n\t}\n\n\t/* Field to match with end */\n\tstart->end_match_field = tep_find_field(start->event, end_match_field);\n\tif (!start->end_match_field)\n\t\tdie(\"Event: %s does not have field %s\",\n\t\t    start->event->name, end_match_field);\n\n\t/* Field to match with start */\n\tend->start_match_field = tep_find_field(end->event, start_match_field);\n\tif (!end->start_match_field)\n\t\tdie(\"Event: %s does not have field %s\",\n\t\t    end->event->name, start_match_field);\n\n\tstart->migrate = migrate;\n\tstart->global = global;\n\tend->migrate = migrate;\n\tend->global = global;\n}\n\n/**\n * tracecmd_mate_events - match events to profile against\n * @handle: The input handle where the events exist.\n * @start_event: The event that starts the transaction\n * @pid_field: Use this over common_pid (may be NULL to use common_pid)\n * @end_match_field: The field that matches the end events @start_match_field\n * @end_event: The event that ends the transaction\n * @start_match_field: The end event field that matches start's @end_match_field\n * @migrate: Can the transaction switch CPUs? 1 for yes, 0 for no\n * @global: The events are global and not per task\n */\nvoid tracecmd_mate_events(struct tracecmd_input *handle,\n\t\t\t  struct tep_event *start_event,\n\t\t\t  const char *pid_field, const char *end_match_field,\n\t\t\t  struct tep_event *end_event,\n\t\t\t  const char *start_match_field,\n\t\t\t  int migrate, int global)\n{\n\tstruct handle_data *h;\n\tstruct event_data *start;\n\tstruct event_data *end;\n\n\tfor (h = handles; h; h = h->next) {\n\t\tif (h->handle == handle)\n\t\t\tbreak;\n\t}\n\tif (!h)\n\t\tdie(\"Handle not found for trace profile\");\n\n\tstart = add_event(h, start_event->system, start_event->name,\n\t\t\t  EVENT_TYPE_USER_MATE);\n\n\tend = add_event(h, end_event->system, end_event->name,\n\t\t\tEVENT_TYPE_USER_MATE);\n\n\tif (!start || !end)\n\t\treturn;\n\n\tmate_events(h, start, pid_field, end_match_field, end, start_match_field,\n\t\t    migrate, global);\n}\n\nstatic void func_print(struct trace_seq *s, struct event_hash *event_hash)\n{\n\tconst char *func;\n\n\tfunc = tep_find_function(event_hash->event_data->event->tep,\n\t\t\t\t event_hash->val);\n\tif (func)\n\t\ttrace_seq_printf(s, \"func: %s()\", func);\n\telse\n\t\ttrace_seq_printf(s, \"func: 0x%llx\", event_hash->val);\n}\n\nstatic void syscall_print(struct trace_seq *s, struct event_hash *event_hash)\n{\n#ifndef NO_AUDIT\n\tconst char *name = NULL;\n\tint machine;\n\n\tmachine = audit_detect_machine();\n\tif (machine < 0)\n\t\tgoto fail;\n\tname = audit_syscall_to_name(event_hash->val, machine);\n\tif (!name)\n\t\tgoto fail;\n\ttrace_seq_printf(s, \"syscall:%s\", name);\n\treturn;\nfail:\n#endif\n\ttrace_seq_printf(s, \"%s:%d\", event_hash->event_data->event->name,\n\t\t\t (int)event_hash->val);\n}\n\n/* From Linux include/linux/interrupt.h */\n#define SOFTIRQS\t\t\t\t\\\n\t\tC(HI),\t\t\t\t\\\n\t\tC(TIMER),\t\t\t\\\n\t\tC(NET_TX),\t\t\t\\\n\t\tC(NET_RX),\t\t\t\\\n\t\tC(BLOCK),\t\t\t\\\n\t\tC(BLOCK_IOPOLL),\t\t\\\n\t\tC(TASKLET),\t\t\t\\\n\t\tC(SCHED),\t\t\t\\\n\t\tC(HRTIMER),\t\t\t\\\n\t\tC(RCU),\t\t\t\t\\\n\t\tC(NR),\n\n#undef C\n#define C(a)\ta##_SOFTIRQ\n\nenum { SOFTIRQS };\n\n#undef C\n#define C(a)\t#a\n\nstatic const char *softirq_map[] = { SOFTIRQS };\n\nstatic void softirq_print(struct trace_seq *s, struct event_hash *event_hash)\n{\n\tint softirq = (int)event_hash->val;\n\n\tif (softirq < NR_SOFTIRQ)\n\t\ttrace_seq_printf(s, \"%s:%s\", event_hash->event_data->event->name,\n\t\t\t\t softirq_map[softirq]);\n\telse\n\t\ttrace_seq_printf(s, \"%s:%d\", event_hash->event_data->event->name,\n\t\t\t\t softirq);\n}\n\nstatic void sched_switch_print(struct trace_seq *s, struct event_hash *event_hash)\n{\n\tconst char states[] = TASK_STATE_TO_CHAR_STR;\n\tint i;\n\n\ttrace_seq_printf(s, \"%s:\", event_hash->event_data->event->name);\n\n\tif (event_hash->val) {\n\t\tint val = event_hash->val;\n\n\t\tfor (i = 0; val && i < sizeof(states) - 1; i++, val >>= 1) {\n\t\t\tif (val & 1)\n\t\t\t\ttrace_seq_putc(s, states[i+1]);\n\t\t}\n\t} else\n\t\ttrace_seq_putc(s, 'R');\n}\n\nstatic int handle_sched_switch_event(struct handle_data *h,\n\t\t\t\t     unsigned long long pid,\n\t\t\t\t     struct event_data *event_data,\n\t\t\t\t     struct tep_record *record, int cpu)\n{\n\tstruct task_data *task;\n\tunsigned long long prev_pid;\n\tunsigned long long prev_state;\n\tunsigned long long next_pid;\n\tstruct start_data *start;\n\n\t/* pid_field holds prev_pid, data_field holds prev_state */\n\ttep_read_number_field(event_data->pid_field,\n\t\t\t      record->data, &prev_pid);\n\n\ttep_read_number_field(event_data->data_field,\n\t\t\t\t record->data, &prev_state);\n\n\t/* only care about real states */\n\tprev_state &= TASK_STATE_MAX - 1;\n\n\t/* end_match_field holds next_pid */\n\ttep_read_number_field(event_data->end_match_field,\n\t\t\t      record->data, &next_pid);\n\n\ttask = find_task(h, prev_pid);\n\tif (!task)\n\t\treturn -1;\n\tif (!task->comm)\n\t\tadd_task_comm(task, h->switch_prev_comm, record);\n\n\tif (prev_state)\n\t\ttask->sleeping = 1;\n\telse\n\t\ttask->sleeping = 0;\n\n\t/* task is being scheduled out. prev_state tells why */\n\tstart = add_start(task, event_data, record, prev_pid, prev_state);\n\ttask->last_start = start;\n\ttask->last_event = NULL;\n\n\ttask = find_task(h, next_pid);\n\tif (!task)\n\t\treturn -1;\n\n\tif (!task->comm)\n\t\tadd_task_comm(task, h->switch_next_comm, record);\n\n\t/*\n\t * If the next task was blocked, it required a wakeup to\n\t * restart, and there should be one.\n\t * But if it was preempted, we look for the previous sched switch.\n\t * Unfortunately, we have to look for both types of events as\n\t * we do not know why next_pid scheduled out.\n\t *\n\t * event_data->start holds the sched_wakeup event data.\n\t */\n\tfind_and_update_start(task, event_data->start, record->ts, next_pid);\n\n\t/* Look for this task if it was preempted (no wakeup found). */\n\tfind_and_update_start(task, event_data, record->ts, next_pid);\n\n\treturn 0;\n}\n\nstatic int handle_stacktrace_event(struct handle_data *h,\n\t\t\t\t   unsigned long long pid,\n\t\t\t\t   struct event_data *event_data,\n\t\t\t\t   struct tep_record *record, int cpu)\n{\n\tstruct task_data *orig_task;\n\tstruct task_data *proxy;\n\tstruct task_data *task;\n\tunsigned long long size;\n\tstruct event_hash *event_hash;\n\tstruct start_data *start;\n\tvoid *caller;\n\n\ttask = find_task(h, pid);\n\tif (!task)\n\t\treturn -1;\n\n\tif (task->last_stack) {\n\t\ttracecmd_free_record(task->last_stack);\n\t\ttask->last_stack = NULL;\n\t}\n\n\tif ((proxy = task->proxy)) {\n\t\ttask->proxy = NULL;\n\t\torig_task = task;\n\t\ttask = proxy;\n\t}\n\n\tif (!task->last_start && !task->last_event) {\n\t\t/*\n\t\t * Save this stack in case function graph needs it.\n\t\t * Need the original task, not a proxy.\n\t\t */\n\t\tif (proxy)\n\t\t\ttask = orig_task;\n\t\ttracecmd_record_ref(record);\n\t\ttask->last_stack = record;\n\t\treturn 0;\n\t}\n\n\t/*\n\t * start_match_field holds the size.\n\t * data_field holds the caller location.\n\t */\n\tsize = record->size - event_data->data_field->offset;\n\tcaller = record->data + event_data->data_field->offset;\n\n\t/*\n\t * If there's a \"start\" then don't add the stack until\n\t * it finds a matching \"end\".\n\t */\n\tif ((start = task->last_start)) {\n\t\ttracecmd_record_ref(record);\n\t\tstart->stack.record = record;\n\t\tstart->stack.size = size;\n\t\tstart->stack.caller = caller;\n\t\ttask->last_start = NULL;\n\t\ttask->last_event = NULL;\n\t\treturn 0;\n\t}\n\n\tevent_hash = task->last_event;\n\ttask->last_event = NULL;\n\n\tadd_event_stack(event_hash, caller, size, event_hash->last_time,\n\t\t\trecord->ts);\n\t\n\treturn 0;\n}\n\nstatic int handle_fgraph_entry_event(struct handle_data *h,\n\t\t\t\t    unsigned long long pid,\n\t\t\t\t    struct event_data *event_data,\n\t\t\t\t    struct tep_record *record, int cpu)\n{\n\tunsigned long long size;\n\tstruct start_data *start;\n\tstruct task_data *task;\n\tvoid *caller;\n\n\ttask = handle_start_event(h, event_data, record, pid);\n\tif (!task)\n\t\treturn -1;\n\n\t/*\n\t * If a stack trace hasn't been used for a previous task,\n\t * then it could be a function trace that we can use for\n\t * the function graph. But stack traces come before the function\n\t * graph events (unfortunately). So we need to attach the previous\n\t * stack trace (if there is one) to this start event.\n\t */\n\tif (task->last_stack) {\n\t\tstart = task->last_start;\n\t\trecord = task->last_stack;\n\t\tsize = record->size - stacktrace_event->data_field->offset;\n\t\tcaller = record->data + stacktrace_event->data_field->offset;\n\t\tstart->stack.record = record;\n\t\tstart->stack.size = size;\n\t\tstart->stack.caller = caller;\n\t\ttask->last_stack = NULL;\n\t\ttask->last_event = NULL;\n\t}\n\n\t/* Do not map stacks after this event to this event */\n\ttask->last_start = NULL;\n\n\treturn 0;\n}\n\nstatic int handle_fgraph_exit_event(struct handle_data *h,\n\t\t\t\t    unsigned long long pid,\n\t\t\t\t    struct event_data *event_data,\n\t\t\t\t    struct tep_record *record, int cpu)\n{\n\tstruct task_data *task;\n\n\ttask = handle_end_event(h, event_data, record, pid);\n\tif (!task)\n\t\treturn -1;\n\t/* Do not match stacks with function graph exit events */\n\ttask->last_event = NULL;\n\n\treturn 0;\n}\n\nstatic int handle_process_exec(struct handle_data *h,\n\t\t\t       unsigned long long pid,\n\t\t\t       struct event_data *event_data,\n\t\t\t       struct tep_record *record, int cpu)\n{\n\tstruct task_data *task;\n\tunsigned long long val;\n\n\t/* Task has execed, remove the comm for it */\n\tif (event_data->data_field) {\n\t\ttep_read_number_field(event_data->data_field,\n\t\t\t\t      record->data, &val);\n\t\tpid = val;\n\t}\n\n\ttask = find_task(h, pid);\n\tif (!task)\n\t\treturn -1;\n\n\tfree(task->comm);\n\ttask->comm = NULL;\n\n\treturn 0;\n}\n\nstatic int handle_sched_wakeup_event(struct handle_data *h,\n\t\t\t\t     unsigned long long pid,\n\t\t\t\t     struct event_data *event_data,\n\t\t\t\t     struct tep_record *record, int cpu)\n{\n\tstruct task_data *proxy;\n\tstruct task_data *task = NULL;\n\tstruct start_data *start;\n\tunsigned long long success;\n\n\tproxy = find_task(h, pid);\n\tif (!proxy)\n\t\treturn -1;\n\n\t/* If present, data_field holds \"success\" */\n\tif (event_data->data_field) {\n\t\ttep_read_number_field(event_data->data_field,\n\t\t\t\t      record->data, &success);\n\n\t\t/* If not a successful wakeup, ignore this */\n\t\tif (!success)\n\t\t\treturn 0;\n\t}\n\n\ttep_read_number_field(event_data->pid_field,\n\t\t\t      record->data, &pid);\n\n\ttask = find_task(h, pid);\n\tif (!task)\n\t\treturn -1;\n\n\tif (!task->comm)\n\t\tadd_task_comm(task, h->wakeup_comm, record);\n\n\t/* if the task isn't sleeping, then ignore the wake up */\n\tif (!task->sleeping) {\n\t\t/* Ignore any following stack traces */\n\t\tproxy->proxy = NULL;\n\t\tproxy->last_start = NULL;\n\t\tproxy->last_event = NULL;\n\t\treturn 0;\n\t}\n\n\t/* It's being woken up */\n\ttask->sleeping = 0;\n\n\t/*\n\t * We need the stack trace to be hooked to the woken up\n\t * task, not the waker.\n\t */\n\tproxy->proxy = task;\n\n\t/* There should be a blocked schedule out of this task */\n\tfind_and_update_start(task, event_data->start, record->ts, pid);\n\n\t/* Set this up for timing how long the wakeup takes */\n\tstart = add_start(task, event_data, record, pid, pid);\n\ttask->last_event = NULL;\n\ttask->last_start = start;\n\n\treturn 0;\n}\n\nvoid trace_init_profile(struct tracecmd_input *handle, struct hook_list *hook,\n\t\t\tint global)\n{\n\tstruct tep_handle *pevent = tracecmd_get_tep(handle);\n\tstruct tep_format_field **fields;\n\tstruct handle_data *h;\n\tstruct event_data *event_data;\n\tstruct event_data *sched_switch;\n\tstruct event_data *sched_wakeup;\n\tstruct event_data *irq_entry;\n\tstruct event_data *irq_exit;\n\tstruct event_data *softirq_entry;\n\tstruct event_data *softirq_exit;\n\tstruct event_data *softirq_raise;\n\tstruct event_data *fgraph_entry;\n\tstruct event_data *fgraph_exit;\n\tstruct event_data *syscall_enter;\n\tstruct event_data *syscall_exit;\n\tstruct event_data *process_exec;\n\tstruct event_data *start_event;\n\tstruct event_data *end_event;\n\tstruct tep_event **events;\n\tint ret;\n\tint i;\n\n\ttracecmd_set_show_data_func(handle, trace_profile_record);\n\th = malloc(sizeof(*h));\n\tif (!h) {\n\t\twarning(\"Could not allocate handle\");\n\t\treturn;\n\t};\n\tmemset(h, 0, sizeof(*h));\n\th->next = handles;\n\thandles = h;\n\n\ttcmd_hash_init(&h->task_hash, 1024);\n\ttcmd_hash_init(&h->events, 1024);\n\ttcmd_hash_init(&h->group_hash, 512);\n\n\th->handle = handle;\n\th->pevent = pevent;\n\n\th->cpus = tracecmd_cpus(handle);\n\n\t/*\n\t * For streaming profiling, cpus will not be set up yet.\n\t * In this case, we simply use the number of cpus on the\n\t * system.\n\t */\n\tif (!h->cpus)\n\t\th->cpus = tracecmd_count_cpus();\n\n\tlist_head_init(&h->migrate_starts);\n\th->cpu_starts = malloc(sizeof(*h->cpu_starts) * h->cpus);\n\tif (!h->cpu_starts)\n\t\tgoto free_handle;\n\n\tfor (i = 0; i < h->cpus; i++)\n\t\tlist_head_init(&h->cpu_starts[i]);\n\n\th->cpu_data = malloc(h->cpus * sizeof(*h->cpu_data));\n\tif (!h->cpu_data)\n\t\tgoto free_starts;\n\n\tmemset(h->cpu_data, 0, h->cpus * sizeof(h->cpu_data));\n\n\th->global_task = malloc(sizeof(struct task_data));\n\tif (!h->global_task)\n\t\tgoto free_data;\n\n\tmemset(h->global_task, 0, sizeof(struct task_data));\n\tinit_task(h, h->global_task);\n\th->global_task->comm = strdup(\"Global Events\");\n\tif (!h->global_task->comm)\n\t\tdie(\"malloc\");\n\th->global_task->pid = -1;\n\n\th->global_percpu_tasks = calloc(h->cpus, sizeof(struct task_data));\n\tif (!h->global_percpu_tasks)\n\t\tdie(\"malloc\");\n\tfor (i = 0; i < h->cpus; i++) {\n\t\tinit_task(h, &h->global_percpu_tasks[i]);\n\t\tret = asprintf(&h->global_percpu_tasks[i].comm,\n\t\t\t       \"Global CPU[%d] Events\", i);\n\t\tif (ret < 0)\n\t\t\tdie(\"malloc\");\n\t\th->global_percpu_tasks[i].pid = -1 - i;\n\t}\n\n\tirq_entry = add_event(h, \"irq\", \"irq_handler_entry\", EVENT_TYPE_IRQ);\n\tirq_exit = add_event(h, \"irq\", \"irq_handler_exit\", EVENT_TYPE_IRQ);\n\tsoftirq_entry = add_event(h, \"irq\", \"softirq_entry\", EVENT_TYPE_SOFTIRQ);\n\tsoftirq_exit = add_event(h, \"irq\", \"softirq_exit\", EVENT_TYPE_SOFTIRQ);\n\tsoftirq_raise = add_event(h, \"irq\", \"softirq_raise\", EVENT_TYPE_SOFTIRQ_RAISE);\n\tsched_wakeup = add_event(h, \"sched\", \"sched_wakeup\", EVENT_TYPE_WAKEUP);\n\tsched_switch = add_event(h, \"sched\", \"sched_switch\", EVENT_TYPE_SCHED_SWITCH);\n\tfgraph_entry = add_event(h, \"ftrace\", \"funcgraph_entry\", EVENT_TYPE_FUNC);\n\tfgraph_exit = add_event(h, \"ftrace\", \"funcgraph_exit\", EVENT_TYPE_FUNC);\n\tsyscall_enter = add_event(h, \"raw_syscalls\", \"sys_enter\", EVENT_TYPE_SYSCALL);\n\tsyscall_exit = add_event(h, \"raw_syscalls\", \"sys_exit\", EVENT_TYPE_SYSCALL);\n\n\tprocess_exec = add_event(h, \"sched\", \"sched_process_exec\",\n\t\t\t\t EVENT_TYPE_PROCESS_EXEC);\n\n\tstacktrace_event = add_event(h, \"ftrace\", \"kernel_stack\", EVENT_TYPE_STACK);\n\tif (stacktrace_event) {\n\t\tstacktrace_event->handle_event = handle_stacktrace_event;\n\n\t\tstacktrace_event->data_field = tep_find_field(stacktrace_event->event,\n\t\t\t\t\t\t\t    \"caller\");\n\t\tif (!stacktrace_event->data_field)\n\t\t\tdie(\"Event: %s does not have field caller\",\n\t\t\t    stacktrace_event->event->name);\n\t}\n\n\tif (process_exec) {\n\t\tprocess_exec->handle_event = handle_process_exec;\n\t\tprocess_exec->data_field = tep_find_field(process_exec->event,\n\t\t\t\t\t\t\t     \"old_pid\");\n\t}\n\n\tif (sched_switch) {\n\t\tsched_switch->handle_event = handle_sched_switch_event;\n\t\tsched_switch->data_field = tep_find_field(sched_switch->event,\n\t\t\t\t\t\t\t     \"prev_state\");\n\t\tif (!sched_switch->data_field)\n\t\t\tdie(\"Event: %s does not have field prev_state\",\n\t\t\t    sched_switch->event->name);\n\n\t\th->switch_prev_comm = tep_find_field(sched_switch->event,\n\t\t\t\t\t\t\t\"prev_comm\");\n\t\tif (!h->switch_prev_comm)\n\t\t\tdie(\"Event: %s does not have field prev_comm\",\n\t\t\t    sched_switch->event->name);\n\n\t\th->switch_next_comm = tep_find_field(sched_switch->event,\n\t\t\t\t\t\t\t\"next_comm\");\n\t\tif (!h->switch_next_comm)\n\t\t\tdie(\"Event: %s does not have field next_comm\",\n\t\t\t    sched_switch->event->name);\n\n\t\tsched_switch->print_func = sched_switch_print;\n\t}\n\n\tif (sched_switch && sched_wakeup) {\n\t\tmate_events(h, sched_switch, \"prev_pid\", \"next_pid\", \n\t\t\t    sched_wakeup, \"pid\", 1, 0);\n\t\tmate_events(h, sched_wakeup, \"pid\", \"pid\",\n\t\t\t    sched_switch, \"prev_pid\", 1, 0);\n\t\tsched_wakeup->handle_event = handle_sched_wakeup_event;\n\n\t\t/* The 'success' field may or may not be present */\n\t\tsched_wakeup->data_field = tep_find_field(sched_wakeup->event,\n\t\t\t\t\t\t\t     \"success\");\n\n\t\th->wakeup_comm = tep_find_field(sched_wakeup->event, \"comm\");\n\t\tif (!h->wakeup_comm)\n\t\t\tdie(\"Event: %s does not have field comm\",\n\t\t\t    sched_wakeup->event->name);\n\t}\n\n\tif (irq_entry && irq_exit)\n\t\tmate_events(h, irq_entry, NULL, \"irq\", irq_exit, \"irq\", 0, global);\n\n\tif (softirq_entry)\n\t\tsoftirq_entry->print_func = softirq_print;\n\n\tif (softirq_exit)\n\t\tsoftirq_exit->print_func = softirq_print;\n\n\tif (softirq_raise)\n\t\tsoftirq_raise->print_func = softirq_print;\n\n\tif (softirq_entry && softirq_exit)\n\t\tmate_events(h, softirq_entry, NULL, \"vec\", softirq_exit, \"vec\",\n\t\t\t    0, global);\n\n\tif (softirq_entry && softirq_raise)\n\t\tmate_events(h, softirq_raise, NULL, \"vec\", softirq_entry, \"vec\",\n\t\t\t    0, global);\n\n\tif (fgraph_entry && fgraph_exit) {\n\t\tmate_events(h, fgraph_entry, NULL, \"func\", fgraph_exit, \"func\", 1, 0);\n\t\tfgraph_entry->handle_event = handle_fgraph_entry_event;\n\t\tfgraph_exit->handle_event = handle_fgraph_exit_event;\n\t\tfgraph_entry->print_func = func_print;\n\t}\n\n\tif (syscall_enter && syscall_exit) {\n\t\tmate_events(h, syscall_enter, NULL, \"id\", syscall_exit, \"id\", 1, 0);\n\t\tsyscall_enter->print_func = syscall_print;\n\t\tsyscall_exit->print_func = syscall_print;\n\t}\n\n\tevents = tep_list_events(pevent, TEP_EVENT_SORT_ID);\n\tif (!events)\n\t\tdie(\"malloc\");\n\n\t/* Add some other events */\n\tevent_data = add_event(h, \"ftrace\", \"function\", EVENT_TYPE_FUNC);\n\tif (event_data) {\n\t\tevent_data->data_field =\n\t\t\ttep_find_field(event_data->event, \"ip\");\n\t}\n\n\t/* Add any user defined hooks */\n\tfor (; hook; hook = hook->next) {\n\t\tstart_event = add_event(h, hook->start_system, hook->start_event,\n\t\t\t\t\tEVENT_TYPE_USER_MATE);\n\t\tend_event = add_event(h, hook->end_system, hook->end_event,\n\t\t\t\t      EVENT_TYPE_USER_MATE);\n\t\tif (!start_event) {\n\t\t\twarning(\"Event %s not found\", hook->start_event);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!end_event) {\n\t\t\twarning(\"Event %s not found\", hook->end_event);\n\t\t\tcontinue;\n\t\t}\n\t\tmate_events(h, start_event, hook->pid, hook->start_match,\n\t\t\t    end_event, hook->end_match, hook->migrate,\n\t\t\t    hook->global);\n\t}\n\n\t/* Now add any defined event that we haven't processed */\n\tfor (i = 0; events[i]; i++) {\n\t\tevent_data = find_event_data(h, events[i]->id);\n\t\tif (event_data)\n\t\t\tcontinue;\n\n\t\tevent_data = add_event(h, events[i]->system, events[i]->name,\n\t\t\t\t       EVENT_TYPE_UNDEFINED);\n\n\t\tfields = tep_event_fields(events[i]);\n\t\tif (!fields)\n\t\t\tdie(\"malloc\");\n\n\t\tif (fields[0])\n\t\t\tevent_data->data_field = fields[0];\n\n\t\tfree(fields);\n\t}\n\treturn;\n\n free_data:\n\tfree(h->cpu_data);\n free_starts:\n\tfree(h->cpu_starts);\n free_handle:\n\thandles = h->next;\n\tfree(h);\n\twarning(\"Failed handle allocations\");\n}\n\nstatic void output_event_stack(struct tep_handle *pevent, struct stack_data *stack)\n{\n\tint longsize = tep_get_long_size(pevent);\n\tunsigned long long val;\n\tconst char *func;\n\tunsigned long long stop = -1ULL;\n\tvoid *ptr;\n\tint i;\n\n\tif (longsize < 8)\n\t\tstop &= (1ULL << (longsize * 8)) - 1;\n\n\tif (stack->count)\n\t\tstack->time_avg = stack->time / stack->count;\n\n\tprintf(\"     <stack> %lld total:%lld min:%lld(ts:%lld.%06lld) max:%lld(ts:%lld.%06lld) avg=%lld\\n\",\n\t       stack->count, stack->time, stack->time_min,\n\t       nsecs_per_sec(stack->ts_min), mod_to_usec(stack->ts_min),\n\t       stack->time_max,\n\t       nsecs_per_sec(stack->ts_max), mod_to_usec(stack->ts_max),\n\t       stack->time_avg);\n\n\tfor (i = 0; i < stack->size; i += longsize) {\n\t\tptr = stack->caller + i;\n\t\tswitch (longsize) {\n\t\tcase 4:\n\t\t\t/* todo, read value from pevent */\n\t\t\tval = *(unsigned int *)ptr;\n\t\t\tbreak;\n\t\tcase 8:\n\t\t\tval = *(unsigned long long *)ptr;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tdie(\"Strange long size %d\", longsize);\n\t\t}\n\t\tif (val == stop)\n\t\t\tbreak;\n\t\tfunc = tep_find_function(pevent, val);\n\t\tif (func)\n\t\t\tprintf(\"       => %s (0x%llx)\\n\", func, val);\n\t\telse\n\t\t\tprintf(\"       => 0x%llx\\n\", val);\n\t}\n}\n\nstruct stack_chain {\n\tstruct stack_chain *children;\n\tunsigned long long\tval;\n\tunsigned long long\ttime;\n\tunsigned long long\ttime_min;\n\tunsigned long long\tts_min;\n\tunsigned long long\ttime_max;\n\tunsigned long long\tts_max;\n\tunsigned long long\ttime_avg;\n\tunsigned long long\tcount;\n\tint\t\t\tpercent;\n\tint\t\t\tnr_children;\n};\n\nstatic int compare_chains(const void *a, const void *b)\n{\n\tconst struct stack_chain * A = a;\n\tconst struct stack_chain * B = b;\n\n\tif (A->time > B->time)\n\t\treturn -1;\n\tif (A->time < B->time)\n\t\treturn 1;\n\t/* If stacks don't use time, then use count */\n\tif (A->count > B->count)\n\t\treturn -1;\n\tif (A->count < B->count)\n\t\treturn 1;\n\treturn 0;\n}\n\nstatic int calc_percent(unsigned long long val, unsigned long long total)\n{\n\treturn (val * 100 + total / 2) / total;\n}\n\nstatic int stack_overflows(struct stack_data *stack, int longsize, int level)\n{\n\treturn longsize * level > stack->size - longsize;\n}\n\nstatic unsigned long long\nstack_value(struct stack_data *stack, int longsize, int level)\n{\n\tvoid *ptr;\n\n\tptr = &stack->caller[longsize * level];\n\treturn longsize == 8 ? *(u64 *)ptr : *(unsigned *)ptr;\n}\n\nstatic struct stack_chain *\nmake_stack_chain(struct stack_data **stacks, int cnt, int longsize, int level,\n\t\t int *nr_children)\n{\n\tstruct stack_chain *chain;\n\tunsigned long long\ttotal_time = 0;\n\tunsigned long long\ttotal_count = 0;\n\tunsigned long long\ttime;\n\tunsigned long long\ttime_min;\n\tunsigned long long\tts_min;\n\tunsigned long long\ttime_max;\n\tunsigned long long\tts_max;\n\tunsigned long long\tcount;\n\tunsigned long long\tstop = -1ULL;\n\tint nr_chains = 0;\n\tu64 last = 0;\n\tu64 val;\n\tint start;\n\tint i;\n\tint x;\n\n\tif (longsize < 8)\n\t\tstop &= (1ULL << (longsize * 8)) - 1;\n\n\t/* First find out how many diffs there are */\n\tfor (i = 0; i < cnt; i++) {\n\t\tif (stack_overflows(stacks[i], longsize, level))\n\t\t\tcontinue;\n\n\t\tval = stack_value(stacks[i], longsize, level);\n\n\t\tif (val == stop)\n\t\t\tcontinue;\n\n\t\tif (!nr_chains || val != last)\n\t\t\tnr_chains++;\n\t\tlast = val;\n\t}\n\n\tif (!nr_chains) {\n\t\t*nr_children = 0;\n\t\treturn NULL;\n\t}\n\n\tchain = malloc(sizeof(*chain) * nr_chains);\n\tif (!chain) {\n\t\twarning(\"Could not allocate chain\");\n\t\treturn NULL;\n\t}\n\tmemset(chain, 0, sizeof(*chain) * nr_chains);\n\n\tx = 0;\n\tcount = 0;\n\tstart = 0;\n\ttime = 0;\n\ttime_min = 0;\n\ttime_max = 0;\n\n\tfor (i = 0; i < cnt; i++) {\n\t\tif (stack_overflows(stacks[i], longsize, level)) {\n\t\t\tstart = i+1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = stack_value(stacks[i], longsize, level);\n\n\t\tif (val == stop) {\n\t\t\tstart = i+1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tcount += stacks[i]->count;\n\t\ttime += stacks[i]->time;\n\t\tif (stacks[i]->time_max > time_max) {\n\t\t\ttime_max = stacks[i]->time_max;\n\t\t\tts_max = stacks[i]->ts_max;\n\t\t}\n\t\tif (i == start || stacks[i]->time_min < time_min) {\n\t\t\ttime_min = stacks[i]->time_min;\n\t\t\tts_min = stacks[i]->ts_min;\n\t\t}\n\t\tif (i == cnt - 1 ||\n\t\t    stack_overflows(stacks[i+1], longsize, level) ||\n\t\t    val != stack_value(stacks[i+1], longsize, level)) {\n\n\t\t\ttotal_time += time;\n\t\t\ttotal_count += count;\n\t\t\tchain[x].val = val;\n\t\t\tchain[x].time_avg = time / count;\n\t\t\tchain[x].count = count;\n\t\t\tchain[x].time = time;\n\t\t\tchain[x].time_min = time_min;\n\t\t\tchain[x].ts_min = ts_min;\n\t\t\tchain[x].time_max = time_max;\n\t\t\tchain[x].ts_max = ts_max;\n\t\t\tchain[x].children =\n\t\t\t\tmake_stack_chain(&stacks[start], (i - start) + 1,\n\t\t\t\t\t\t longsize, level+1,\n\t\t\t\t\t\t &chain[x].nr_children);\n\t\t\tx++;\n\t\t\tstart = i + 1;\n\t\t\tcount = 0;\n\t\t\ttime = 0;\n\t\t\ttime_min = 0;\n\t\t\ttime_max = 0;\n\t\t}\n\t}\n\n\tqsort(chain, nr_chains, sizeof(*chain), compare_chains);\n\n\t*nr_children = nr_chains;\n\n\t/* Should never happen */\n\tif (!total_time && !total_count)\n\t\treturn chain;\n\n\n\t/* Now calculate percentage */\n\ttime = 0;\n\tfor (i = 0; i < nr_chains; i++) {\n\t\tif (total_time)\n\t\t\tchain[i].percent = calc_percent(chain[i].time, total_time);\n\t\t/* In case stacks don't have time */\n\t\telse if (total_count)\n\t\t\tchain[i].percent = calc_percent(chain[i].count, total_count);\n\t}\n\n\treturn chain;\n}\n\nstatic void free_chain(struct stack_chain *chain, int nr_chains)\n{\n\tint i;\n\n\tif (!chain)\n\t\treturn;\n\n\tfor (i = 0; i < nr_chains; i++)\n\t\tfree_chain(chain[i].children, chain[i].nr_children);\n\n\tfree(chain);\n}\n\n#define INDENT\t5\n\nstatic void print_indent(int level, unsigned long long mask)\n{\n\tchar line;\n\tint p;\n\n\tfor (p = 0; p < level + 1; p++) {\n\t\tif (mask & (1ULL << p))\n\t\t\tline = '|';\n\t\telse\n\t\t\tline = ' ';\n\t\tprintf(\"%*c \", INDENT, line);\n\t}\n}\n\nstatic void print_chain_func(struct tep_handle *pevent, struct stack_chain *chain)\n{\n\tunsigned long long val = chain->val;\n\tconst char *func;\n\n\tfunc = tep_find_function(pevent, val);\n\tif (func)\n\t\tprintf(\"%s (0x%llx)\\n\", func, val);\n\telse\n\t\tprintf(\"0x%llx\\n\", val);\n}\n\nstatic void output_chain(struct tep_handle *pevent, struct stack_chain *chain, int level,\n\t\t\t int nr_chains, unsigned long long *mask)\n{\n\tstruct stack_chain *child;\n\tint nr_children;\n\tint i;\n\tchar line = '|';\n\n\tif (!nr_chains)\n\t\treturn;\n\n\t*mask |= (1ULL << (level + 1));\n\tprint_indent(level + 1, *mask);\n\tprintf(\"\\n\");\n\n\tfor (i = 0; i < nr_chains; i++) {\n\n\t\tprint_indent(level, *mask);\n\n\t\tprintf(\"%*c \", INDENT, '+');\n\n\t\tif (i == nr_chains - 1) {\n\t\t\t*mask &= ~(1ULL << (level + 1));\n\t\t\tline = ' ';\n\t\t}\n\n\t\tprint_chain_func(pevent, &chain[i]);\n\n\t\tprint_indent(level, *mask);\n\n\t\tprintf(\"%*c \", INDENT, line);\n\t\tprintf(\"  %d%% (%lld)\", chain[i].percent, chain[i].count);\n\t\tif (chain[i].time)\n\t\t\tprintf(\" time:%lld max:%lld(ts:%lld.%06lld) min:%lld(ts:%lld.%06lld) avg:%lld\",\n\t\t\t       chain[i].time, chain[i].time_max,\n\t\t\t       nsecs_per_sec(chain[i].ts_max),\n\t\t\t       mod_to_usec(chain[i].ts_max),\n\t\t\t       chain[i].time_min,\n\t\t\t       nsecs_per_sec(chain[i].ts_min),\n\t\t\t       mod_to_usec(chain[i].ts_min),\n\t\t\t       chain[i].time_avg);\n\t\tprintf(\"\\n\");\n\n\t\tfor (child = chain[i].children, nr_children = chain[i].nr_children;\n\t\t     child && nr_children == 1;\n\t\t     nr_children = child->nr_children, child = child->children) {\n\t\t\tprint_indent(level, *mask);\n\t\t\tprintf(\"%*c \", INDENT, line);\n\t\t\tprintf(\"   \");\n\t\t\tprint_chain_func(pevent, child);\n\t\t}\n\n\t\tif (child)\n\t\t\toutput_chain(pevent, child, level+1, nr_children, mask);\n\n\t\tprint_indent(level + 1, *mask);\n\t\tprintf(\"\\n\");\n\t}\n\t*mask &= ~(1ULL << (level + 1));\n\tprint_indent(level, *mask);\n\tprintf(\"\\n\");\n}\n\nstatic int compare_stacks(const void *a, const void *b)\n{\n\tstruct stack_data * const *A = a;\n\tstruct stack_data * const *B = b;\n\tunsigned int sa, sb;\n\tint size;\n\tint i;\n\n\t/* only compare up to the smaller size of the two */\n\tif ((*A)->size > (*B)->size)\n\t\tsize = (*B)->size;\n\telse\n\t\tsize = (*A)->size;\n\n\tfor (i = 0; i < size; i += sizeof(sa)) {\n\t\tsa = *(unsigned *)&(*A)->caller[i];\n\t\tsb = *(unsigned *)&(*B)->caller[i];\n\t\tif (sa > sb)\n\t\t\treturn 1;\n\t\tif (sa < sb)\n\t\t\treturn -1;\n\t}\n\n\t/* They are the same up to size. Then bigger size wins */\n\tif ((*A)->size > (*B)->size)\n\t\treturn 1;\n\tif ((*A)->size < (*B)->size)\n\t\treturn -1;\n\treturn 0;\n}\n\nstatic void output_stacks(struct tep_handle *pevent, struct trace_hash *stack_hash)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\tstruct stack_data **stacks;\n\tstruct stack_chain *chain;\n\tunsigned long long mask = 0;\n\tint nr_chains;\n\tint longsize = tep_get_long_size(pevent);\n\tint nr_stacks;\n\tint i;\n\n\tnr_stacks = 0;\n\ttrace_hash_for_each_bucket(bucket, stack_hash) {\n\t\ttrace_hash_for_each_item(item, bucket) {\n\t\t\tnr_stacks++;\n\t\t}\n\t}\n\n\tstacks = malloc(sizeof(*stacks) * nr_stacks);\n\tif (!stacks) {\n\t\twarning(\"Could not allocate stacks\");\n\t\treturn;\n\t}\n\n\tnr_stacks = 0;\n\ttrace_hash_for_each_bucket(bucket, stack_hash) {\n\t\ttrace_hash_for_each_item(item, bucket) {\n\t\t\tstacks[nr_stacks++] = stack_from_item(item);\n\t\t}\n\t}\n\n\tqsort(stacks, nr_stacks, sizeof(*stacks), compare_stacks);\n\n\tchain = make_stack_chain(stacks, nr_stacks, longsize, 0, &nr_chains);\n\n\toutput_chain(pevent, chain, 0, nr_chains, &mask);\n\n\tif (0)\n\t\tfor (i = 0; i < nr_stacks; i++)\n\t\t\toutput_event_stack(pevent, stacks[i]);\n\n\tfree(stacks);\n\tfree_chain(chain, nr_chains);\n}\n\nstatic void output_event(struct event_hash *event_hash)\n{\n\tstruct event_data *event_data = event_hash->event_data;\n\tstruct tep_handle *pevent = event_data->event->tep;\n\tstruct trace_seq s;\n\n\ttrace_seq_init(&s);\n\n\tif (event_data->print_func)\n\t\tevent_data->print_func(&s, event_hash);\n\telse if (event_data->type == EVENT_TYPE_FUNC)\n\t\tfunc_print(&s, event_hash);\n\telse\n\t\ttrace_seq_printf(&s, \"%s:0x%llx\",\n\t\t\t\t event_data->event->name,\n\t\t\t\t event_hash->val);\n\ttrace_seq_terminate(&s);\n\n\tprintf(\"  Event: %s (%lld)\",\n\t       s.buffer, event_hash->count);\n\n\ttrace_seq_destroy(&s);\n\n\tif (event_hash->time_total) {\n\t\tevent_hash->time_avg = event_hash->time_total / event_hash->count;\n\t\tprintf(\" Total: %lld Avg: %lld Max: %lld(ts:%lld.%06lld) Min:%lld(ts:%lld.%06lld)\",\n\t\t       event_hash->time_total, event_hash->time_avg,\n\t\t       event_hash->time_max,\n\t\t       nsecs_per_sec(event_hash->ts_max),\n\t\t       mod_to_usec(event_hash->ts_max),\n\t\t       event_hash->time_min,\n\t\t       nsecs_per_sec(event_hash->ts_min),\n\t\t       mod_to_usec(event_hash->ts_min));\n\t}\n\tprintf(\"\\n\");\n\n\toutput_stacks(pevent, &event_hash->stacks);\n}\n\nstatic int compare_events(const void *a, const void *b)\n{\n\tstruct event_hash * const *A = a;\n\tstruct event_hash * const *B = b;\n\tconst struct event_data *event_data_a = (*A)->event_data;\n\tconst struct event_data *event_data_b = (*B)->event_data;\n\n\t/* Schedule switch goes first */\n\tif (event_data_a->type == EVENT_TYPE_SCHED_SWITCH) {\n\t\tif (event_data_b->type != EVENT_TYPE_SCHED_SWITCH)\n\t\t\treturn -1;\n\t\t/* lower the state the better */\n\t\tif ((*A)->val > (*B)->val)\n\t\t\treturn 1;\n\t\tif ((*A)->val < (*B)->val)\n\t\t\treturn -1;\n\t\treturn 0;\n\t} else if (event_data_b->type == EVENT_TYPE_SCHED_SWITCH)\n\t\t\treturn 1;\n\n\t/* Wakeups are next */\n\tif (event_data_a->type == EVENT_TYPE_WAKEUP) {\n\t\tif (event_data_b->type != EVENT_TYPE_WAKEUP)\n\t\t\treturn -1;\n\t\treturn 0;\n\t} else if (event_data_b->type == EVENT_TYPE_WAKEUP)\n\t\treturn 1;\n\n\tif (event_data_a->id > event_data_b->id)\n\t\treturn 1;\n\tif (event_data_a->id < event_data_b->id)\n\t\treturn -1;\n\tif ((*A)->time_total > (*B)->time_total)\n\t\treturn -1;\n\tif ((*A)->time_total < (*B)->time_total)\n\t\treturn 1;\n\treturn 0;\n}\n\nstatic void output_task(struct handle_data *h, struct task_data *task)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\tstruct event_hash **events;\n\tconst char *comm;\n\tint nr_events = 0;\n\tint i;\n\n\tif (task->group)\n\t\treturn;\n\n\tif (task->comm)\n\t\tcomm = task->comm;\n\telse\n\t\tcomm = tep_data_comm_from_pid(h->pevent, task->pid);\n\n\tif (task->pid < 0)\n\t\tprintf(\"%s\\n\", task->comm);\n\telse\n\t\tprintf(\"\\ntask: %s-%d\\n\", comm, task->pid);\n\n\ttrace_hash_for_each_bucket(bucket, &task->event_hash) {\n\t\ttrace_hash_for_each_item(item, bucket) {\n\t\t\tnr_events++;\n\t\t}\n\t}\n\n\tevents = malloc(sizeof(*events) * nr_events);\n\tif (!events) {\n\t\twarning(\"Could not allocate events\");\n\t\treturn;\n\t}\n\n\ti = 0;\n\ttrace_hash_for_each_bucket(bucket, &task->event_hash) {\n\t\ttrace_hash_for_each_item(item, bucket) {\n\t\t\tevents[i++] = event_from_item(item);\n\t\t}\n\t}\n\n\tqsort(events, nr_events, sizeof(*events), compare_events);\n\n\tfor (i = 0; i < nr_events; i++)\n\t\toutput_event(events[i]);\n\n\tfree(events);\n}\n\nstatic void output_group(struct handle_data *h, struct group_data *group)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\tstruct event_hash **events;\n\tint nr_events = 0;\n\tint i;\n\n\tprintf(\"\\ngroup: %s\\n\", group->comm);\n\n\ttrace_hash_for_each_bucket(bucket, &group->event_hash) {\n\t\ttrace_hash_for_each_item(item, bucket) {\n\t\t\tnr_events++;\n\t\t}\n\t}\n\n\tevents = malloc(sizeof(*events) * nr_events);\n\tif (!events) {\n\t\twarning(\"Could not allocate events\");\n\t\treturn;\n\t}\n\n\ti = 0;\n\ttrace_hash_for_each_bucket(bucket, &group->event_hash) {\n\t\ttrace_hash_for_each_item(item, bucket) {\n\t\t\tevents[i++] = event_from_item(item);\n\t\t}\n\t}\n\n\tqsort(events, nr_events, sizeof(*events), compare_events);\n\n\tfor (i = 0; i < nr_events; i++)\n\t\toutput_event(events[i]);\n\n\tfree(events);\n}\n\nstatic int compare_tasks(const void *a, const void *b)\n{\n\tstruct task_data * const *A = a;\n\tstruct task_data * const *B = b;\n\n\tif ((*A)->pid > (*B)->pid)\n\t\treturn 1;\n\telse if ((*A)->pid < (*B)->pid)\n\t\treturn -1;\n\treturn 0;\n}\n\nstatic int compare_groups(const void *a, const void *b)\n{\n\tconst char *A = a;\n\tconst char *B = b;\n\n\treturn strcmp(A, B);\n}\n\nstatic void free_event_hash(struct event_hash *event_hash)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\tstruct stack_data *stack;\n\n\ttrace_hash_for_each_bucket(bucket, &event_hash->stacks) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\tstack = stack_from_item(item);\n\t\t\ttrace_hash_del(&stack->hash);\n\t\t\tfree(stack);\n\t\t}\n\t}\n\ttcmd_hash_free(&event_hash->stacks);\n\tfree(event_hash);\n}\n\nstatic void __free_task(struct task_data *task)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\tstruct start_data *start;\n\tstruct event_hash *event_hash;\n\n\tfree(task->comm);\n\n\ttrace_hash_for_each_bucket(bucket, &task->start_hash) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\tstart = start_from_item(item);\n\t\t\tif (start->stack.record)\n\t\t\t\ttracecmd_free_record(start->stack.record);\n\t\t\tlist_del(&start->list);\n\t\t\ttrace_hash_del(item);\n\t\t\tfree(start);\n\t\t}\n\t}\n\ttcmd_hash_free(&task->start_hash);\n\n\ttrace_hash_for_each_bucket(bucket, &task->event_hash) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\tevent_hash = event_from_item(item);\n\t\t\ttrace_hash_del(item);\n\t\t\tfree_event_hash(event_hash);\n\t\t}\n\t}\n\ttcmd_hash_free(&task->event_hash);\n\n\tif (task->last_stack)\n\t\ttracecmd_free_record(task->last_stack);\n}\n\nstatic void free_task(struct task_data *task)\n{\n\t__free_task(task);\n\tfree(task);\n}\n\nstatic void free_group(struct group_data *group)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\tstruct event_hash *event_hash;\n\n\tfree(group->comm);\n\n\ttrace_hash_for_each_bucket(bucket, &group->event_hash) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\tevent_hash = event_from_item(item);\n\t\t\ttrace_hash_del(item);\n\t\t\tfree_event_hash(event_hash);\n\t\t}\n\t}\n\ttcmd_hash_free(&group->event_hash);\n\tfree(group);\n}\n\nstatic void show_global_task(struct handle_data *h,\n\t\t\t     struct task_data *task)\n{\n\tif (tcmd_hash_empty(&task->event_hash))\n\t\treturn;\n\n\toutput_task(h, task);\n}\n\nstatic void output_tasks(struct handle_data *h)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\tstruct task_data **tasks;\n\tint nr_tasks = 0;\n\tint i;\n\n\ttrace_hash_for_each_bucket(bucket, &h->task_hash) {\n\t\ttrace_hash_for_each_item(item, bucket) {\n\t\t\tnr_tasks++;\n\t\t}\n\t}\n\n\ttasks = malloc(sizeof(*tasks) * nr_tasks);\n\tif (!tasks) {\n\t\twarning(\"Could not allocate tasks\");\n\t\treturn;\n\t}\n\n\tnr_tasks = 0;\n\n\ttrace_hash_for_each_bucket(bucket, &h->task_hash) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\ttasks[nr_tasks++] = task_from_item(item);\n\t\t\ttrace_hash_del(item);\n\t\t}\n\t}\n\n\tqsort(tasks, nr_tasks, sizeof(*tasks), compare_tasks);\n\n\tfor (i = 0; i < nr_tasks; i++) {\n\t\toutput_task(h, tasks[i]);\n\t\tfree_task(tasks[i]);\n\t}\n\n\tfree(tasks);\n}\n\nstatic void output_groups(struct handle_data *h)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\tstruct group_data **groups;\n\tint nr_groups = 0;\n\tint i;\n\n\ttrace_hash_for_each_bucket(bucket, &h->group_hash) {\n\t\ttrace_hash_for_each_item(item, bucket) {\n\t\t\tnr_groups++;\n\t\t}\n\t}\n\n\tif (nr_groups == 0)\n\t\treturn;\n\n\tgroups = malloc(sizeof(*groups) * nr_groups);\n\tif (!groups) {\n\t\twarning(\"Could not allocate groups\");\n\t\treturn;\n\t}\n\n\tnr_groups = 0;\n\n\ttrace_hash_for_each_bucket(bucket, &h->group_hash) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\tgroups[nr_groups++] = group_from_item(item);\n\t\t\ttrace_hash_del(item);\n\t\t}\n\t}\n\n\tqsort(groups, nr_groups, sizeof(*groups), compare_groups);\n\n\tfor (i = 0; i < nr_groups; i++) {\n\t\toutput_group(h, groups[i]);\n\t\tfree_group(groups[i]);\n\t}\n\n\tfree(groups);\n}\n\nstatic void output_handle(struct handle_data *h)\n{\n\tint i;\n\n\tshow_global_task(h, h->global_task);\n\tfor (i = 0; i < h->cpus; i++)\n\t\tshow_global_task(h, &h->global_percpu_tasks[i]);\n\n\toutput_groups(h);\n\toutput_tasks(h);\n}\n\nstatic void merge_event_stack(struct event_hash *event,\n\t\t\t      struct stack_data *stack)\n{\n\tstruct stack_data *exist;\n\tstruct trace_hash_item *item;\n\tstruct stack_match match;\n\n\tmatch.caller = stack->caller;\n\tmatch.size = stack->size;\n\titem = tcmd_hash_find(&event->stacks, stack->hash.key, match_stack, &match);\n\tif (!item) {\n\t\ttcmd_hash_add(&event->stacks, &stack->hash);\n\t\treturn;\n\t}\n\texist = stack_from_item(item);\n\texist->count += stack->count;\n\texist->time += stack->time;\n\n\tif (exist->time_max < stack->time_max) {\n\t\texist->time_max = stack->time_max;\n\t\texist->ts_max = stack->ts_max;\n\t}\n\tif (exist->time_min > stack->time_min) {\n\t\texist->time_min = stack->time_min;\n\t\texist->ts_min = stack->ts_min;\n\t}\n\tfree(stack);\n}\n\nstatic void merge_stacks(struct event_hash *exist, struct event_hash *event)\n{\n\tstruct stack_data *stack;\n\tstruct trace_hash_item *item;\n\tstruct trace_hash_item **bucket;\n\n\ttrace_hash_for_each_bucket(bucket, &event->stacks) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\tstack = stack_from_item(item);\n\t\t\ttrace_hash_del(&stack->hash);\n\t\t\tmerge_event_stack(exist, stack);\n\t\t}\n\t}\n}\n\nstatic void merge_event_into_group(struct group_data *group,\n\t\t\t\t   struct event_hash *event)\n{\n\tstruct event_hash *exist;\n\tstruct trace_hash_item *item;\n\tstruct event_data_match edata;\n\tunsigned long long key;\n\n\tif (event->event_data->type == EVENT_TYPE_WAKEUP) {\n\t\tedata.event_data = event->event_data;\n\t\tevent->search_val = 0;\n\t\tevent->val = 0;\n\t\tkey = trace_hash((unsigned long)event->event_data);\n\t} else if (event->event_data->type == EVENT_TYPE_SCHED_SWITCH) {\n\t\tedata.event_data = event->event_data;\n\t\tevent->search_val = event->val;\n\t\tkey = (unsigned long)event->event_data +\n\t\t\t((unsigned long)event->val * 2);\n\t\tkey = trace_hash(key);\n\t} else {\n\t\tkey = event->hash.key;\n\t}\n\n\tedata.event_data = event->event_data;\n\tedata.search_val = event->search_val;\n\tedata.val = event->val;\n\n\titem = tcmd_hash_find(&group->event_hash, key, match_event, &edata);\n\tif (!item) {\n\t\tevent->hash.key = key;\n\t\ttcmd_hash_add(&group->event_hash, &event->hash);\n\t\treturn;\n\t}\n\n\texist = event_from_item(item);\n\texist->count += event->count;\n\texist->time_total += event->time_total;\n\n\tif (exist->time_max < event->time_max) {\n\t\texist->time_max = event->time_max;\n\t\texist->ts_max = event->ts_max;\n\t}\n\tif (exist->time_min > event->time_min) {\n\t\texist->time_min = event->time_min;\n\t\texist->ts_min = event->ts_min;\n\t}\n\n\tmerge_stacks(exist, event);\n\tfree_event_hash(event);\n}\n\nstatic void add_group(struct handle_data *h, struct task_data *task)\n{\n\tunsigned long long key;\n\tstruct trace_hash_item *item;\n\tstruct group_data *grp;\n\tstruct trace_hash_item **bucket;\n\tvoid *data = task->comm;\n\n\tif (!task->comm)\n\t\treturn;\n\n\tkey = trace_hash_str(task->comm);\n\n\titem = tcmd_hash_find(&h->group_hash, key, match_group, data);\n\tif (item) {\n\t\tgrp = group_from_item(item);\n\t} else {\n\t\tgrp = malloc(sizeof(*grp));\n\t\tif (!grp) {\n\t\t\twarning(\"Could not allocate group\");\n\t\t\treturn;\n\t\t}\n\t\tmemset(grp, 0, sizeof(*grp));\n\n\t\tgrp->comm = strdup(task->comm);\n\t\tif (!grp->comm)\n\t\t\tdie(\"strdup\");\n\t\tgrp->hash.key = key;\n\t\ttcmd_hash_add(&h->group_hash, &grp->hash);\n\t\ttcmd_hash_init(&grp->event_hash, 32);\n\t}\n\ttask->group = grp;\n\n\ttrace_hash_for_each_bucket(bucket, &task->event_hash) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\tstruct event_hash *event_hash;\n\n\t\t\tevent_hash = event_from_item(item);\n\t\t\ttrace_hash_del(&event_hash->hash);\n\t\t\tmerge_event_into_group(grp, event_hash);\n\t\t}\n\t}\n}\n\nstatic void merge_tasks(struct handle_data *h)\n{\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\n\tif (!merge_like_comms)\n\t\treturn;\n\n\ttrace_hash_for_each_bucket(bucket, &h->task_hash) {\n\t\ttrace_hash_for_each_item(item, bucket)\n\t\t\tadd_group(h, task_from_item(item));\n\t}\n}\n\nint do_trace_profile(void)\n{\n\tstruct handle_data *h;\n\n\tfor (h = handles; h; h = h->next) {\n\t\tif (merge_like_comms)\n\t\t\tmerge_tasks(h);\n\t\toutput_handle(h);\n\t\ttcmd_hash_free(&h->task_hash);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "tracecmd/trace-read.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <stdarg.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <fcntl.h>\n#include <signal.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <errno.h>\n\n#include \"trace-local.h\"\n#include \"trace-hash.h\"\n#include \"trace-hash-local.h\"\n#include \"kbuffer.h\"\n#include \"list.h\"\n\n/*\n * tep_func_repeat_format is defined as a weak variable in the\n * libtraceevent library function plugin, to allow applications\n * to override the format of the timestamp it prints for the\n * last function that repeated.\n */\nconst char *tep_func_repeat_format;\n\nstatic struct filter_str {\n\tstruct filter_str\t*next;\n\tchar\t\t\t*filter;\n\tint\t\t\tneg;\n} *filter_strings;\nstatic struct filter_str **filter_next = &filter_strings;\n\nstruct event_str {\n\tstruct event_str\t*next;\n\tconst char\t\t*event;\n};\n\nstruct input_files;\n\nstruct handle_list {\n\tstruct list_head\tlist;\n\tstruct tracecmd_input\t*handle;\n\tstruct input_files\t*input_file;\n\tconst char\t\t*file;\n\tint\t\t\tcpus;\n};\nstatic struct list_head handle_list;\n\nstruct input_files {\n\tstruct list_head\tlist;\n\tconst char\t\t*file;\n\tstruct filter_str\t*filter_str;\n\tstruct filter_str\t**filter_str_next;\n\tlong long\t\ttsoffset;\n\tunsigned long long\tts2secs;\n};\nstatic struct list_head input_files;\nstatic struct input_files *last_input_file;\n\nstruct pid_list {\n\tstruct pid_list\t\t*next;\n\tchar\t\t\t*pid;\n\tint\t\t\tfree;\n} *pid_list;\n\nstruct pid_list *comm_list;\n\nstatic unsigned int page_size;\nstatic int input_fd;\nstatic const char *default_input_file = DEFAULT_INPUT_FILE;\nstatic const char *input_file;\nstatic int multi_inputs;\nstatic int max_file_size;\n\nstatic int instances;\n\nstatic int *filter_cpus;\nstatic int nr_filter_cpus;\nstatic int test_filters_mode;\n\nstatic int show_wakeup;\nstatic int wakeup_id;\nstatic int wakeup_new_id;\nstatic int sched_id;\n\nstatic int profile;\n\nstatic int buffer_breaks = 0;\n\nstatic int no_irqs;\nstatic int no_softirqs;\n\nstatic int tsdiff;\nstatic int tscheck;\n\nstatic int latency_format;\nstatic bool raw_format;\nstatic const char *format_type = TEP_PRINT_INFO;\n\nstatic struct tep_format_field *wakeup_task;\nstatic struct tep_format_field *wakeup_success;\nstatic struct tep_format_field *wakeup_new_task;\nstatic struct tep_format_field *wakeup_new_success;\nstatic struct tep_format_field *sched_task;\nstatic struct tep_format_field *sched_prio;\n\nstatic unsigned long long total_wakeup_lat;\nstatic unsigned long wakeup_lat_count;\n\nstatic unsigned long long total_wakeup_rt_lat;\nstatic unsigned long wakeup_rt_lat_count;\n\nstruct wakeup_info {\n\tstruct trace_hash_item\thash;\n\tunsigned long long\tstart;\n\tint\t\t\tpid;\n};\n\nstatic struct hook_list *hooks;\nstatic struct hook_list *last_hook;\n\n#define WAKEUP_HASH_SIZE 1024\nstatic struct trace_hash wakeup_hash;\n\nstatic void print_event_name(struct trace_seq *s, struct tep_event *event)\n{\n\tstatic const char *spaces = \"                    \"; /* 20 spaces */\n\tconst char *name;\n\tint len;\n\n\tname = event ? event->name : \"(NULL)\";\n\n\ttrace_seq_printf(s, \" %s: \", name);\n\n\t/* Space out the event names evenly. */\n\tlen = strlen(name);\n\tif (len < 20)\n\t\ttrace_seq_printf(s, \"%.*s\", 20 - len, spaces);\n}\n\nenum time_fmt {\n\tTIME_FMT_LAT\t\t= 1,\n\tTIME_FMT_NORMAL,\n\tTIME_FMT_TS,\n};\n\nstatic const char *time_format(struct tracecmd_input *handle, enum time_fmt tf)\n{\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\n\tswitch (tf) {\n\tcase TIME_FMT_LAT:\n\t\tif (latency_format)\n\t\t\treturn \"%8.8s-%-5d %3d\";\n\t\treturn \"%16s-%-5d [%03d]\";\n\tdefault:\n\t\tif (tracecmd_get_flags(handle) & TRACECMD_FL_IN_USECS) {\n\t\t\tif (tep_test_flag(tep, TEP_NSEC_OUTPUT))\n\t\t\t\treturn tf == TIME_FMT_NORMAL ? \" %9.1d:\" : \"%9.1d\";\n\t\t\telse\n\t\t\t\treturn tf == TIME_FMT_NORMAL ? \" %6.1000d:\" : \"%6.1000d\";\n\t\t} else\n\t\t\treturn tf == TIME_FMT_NORMAL ? \"%12d:\" : \"%12d\";\n\t}\n}\n\nstatic void print_event(struct trace_seq *s, struct tracecmd_input *handle,\n\t\t\tstruct tep_record *record)\n{\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tstruct tep_event *event;\n\tconst char *lfmt = time_format(handle, TIME_FMT_LAT);\n\tconst char *tfmt = time_format(handle, TIME_FMT_NORMAL);\n\n\tevent = tep_find_event_by_record(tep, record);\n\ttep_print_event(tep, s, record, lfmt, TEP_PRINT_COMM,\n\t\t\tTEP_PRINT_PID, TEP_PRINT_CPU);\n\ttep_print_event(tep, s, record, tfmt, TEP_PRINT_TIME);\n\tprint_event_name(s, event);\n\ttep_print_event(tep, s, record, \"%s\", format_type);\n}\n\n/* Debug variables for testing tracecmd_read_at */\n#define TEST_READ_AT 0\n#if TEST_READ_AT\n#define DO_TEST\nstatic off_t test_read_at_offset;\nstatic int test_read_at_copy = 100;\nstatic int test_read_at_index;\nstatic void show_test(struct tracecmd_input *handle)\n{\n\tstruct tep_record *record;\n\tstruct trace_seq s;\n\n\tif (!test_read_at_offset) {\n\t\tprintf(\"\\nNO RECORD COPIED\\n\");\n\t\treturn;\n\t}\n\n\trecord = tracecmd_read_at(handle, test_read_at_offset, NULL);\n\tprintf(\"\\nHERE'S THE COPY RECORD\\n\");\n\ttrace_seq_init(&s);\n\tprint_event(&s, handle, record);\n\ttrace_seq_do_printf(&s);\n\ttrace_seq_destroy(&s);\n\tprintf(\"\\n\");\n\n\ttracecmd_free_record(record);\n}\n\nstatic void test_save(struct tep_record *record, int cpu)\n{\n\tif (test_read_at_index++ == test_read_at_copy) {\n\t\ttest_read_at_offset = record->offset;\n\t\tprintf(\"\\nUSING THIS RECORD\\n\");\n\t}\n}\n#endif /* TEST_READ_AT */\n\n/* Debug variables for testing tracecmd_set_cpu_at_timestamp */\n#define TEST_AT_TIMESTAMP 0\n#if TEST_AT_TIMESTAMP\n#define DO_TEST\nstatic unsigned long long test_at_timestamp_ts;\nstatic int test_at_timestamp_copy = 100;\nstatic int test_at_timestamp_cpu = -1;\nstatic int test_at_timestamp_index;\nstatic void show_test(struct tracecmd_input *handle)\n{\n\tstruct tep_record *record;\n\tstruct trace_seq s;\n\tint cpu = test_at_timestamp_cpu;\n\n\tif (!test_at_timestamp_ts) {\n\t\tprintf(\"\\nNO RECORD COPIED\\n\");\n\t\treturn;\n\t}\n\n\tif (tracecmd_set_cpu_to_timestamp(handle, cpu, test_at_timestamp_ts))\n\t\treturn;\n\n\trecord = tracecmd_read_data(handle, cpu);\n\tprintf(\"\\nHERE'S THE COPY RECORD with page %p offset=%p\\n\",\n\t       (void *)(record->offset & ~(page_size - 1)),\n\t       (void *)record->offset);\n\ttrace_seq_init(&s);\n\tprint_event(&s, handle, record);\n\ttrace_seq_do_printf(&s);\n\ttrace_seq_destroy(&s);\n\tprintf(\"\\n\");\n\n\ttracecmd_free_record(record);\n}\n\nstatic void test_save(struct tep_record *record, int cpu)\n{\n\tif (test_at_timestamp_index++ == test_at_timestamp_copy) {\n\t\ttest_at_timestamp_ts = record->ts;\n\t\ttest_at_timestamp_cpu = cpu;\n\t\tprintf(\"\\nUSING THIS RECORD page=%p offset=%p\\n\",\n\t\t       (void *)(record->offset & ~(page_size - 1)),\n\t\t       (void *)record->offset);\n\t}\n}\n#endif /* TEST_AT_TIMESTAMP */\n\n#define TEST_FIRST_LAST 0\n#if TEST_FIRST_LAST\n#define DO_TEST\nstatic void show_test(struct tracecmd_input *handle)\n{\n\tstruct tep_record *record;\n\tstruct trace_seq s;\n\tint cpu = 0;\n\n\trecord = tracecmd_read_cpu_first(handle, cpu);\n\tif (!record) {\n\t\tprintf(\"No first record?\\n\");\n\t\treturn;\n\t}\n\n\tprintf(\"\\nHERE'S THE FIRST RECORD with offset %p\\n\",\n\t       (void *)record->offset);\n\ttrace_seq_init(&s);\n\tprint_event(&s, handle, record);\n\ttrace_seq_do_printf(&s);\n\ttrace_seq_destroy(&s);\n\tprintf(\"\\n\");\n\n\ttracecmd_free_record(record);\n\n\trecord = tracecmd_read_cpu_last(handle, cpu);\n\tif (!record) {\n\t\tprintf(\"No last record?\\n\");\n\t\treturn;\n\t}\n\n\tprintf(\"\\nHERE'S THE LAST RECORD with offset %p\\n\",\n\t       (void *)record->offset);\n\ttrace_seq_init(&s);\n\tprint_event(&s, handle, record);\n\ttrace_seq_do_printf(&s);\n\ttrace_seq_destroy(&s);\n\tprintf(\"\\n\");\n\n\ttracecmd_free_record(record);\n}\nstatic void test_save(struct tep_record *record, int cpu)\n{\n}\n#endif /* TEST_FIRST_LAST */\n\n#ifndef DO_TEST\nstatic void show_test(struct tracecmd_input *handle)\n{\n\t/* quiet the compiler */\n\tif (0)\n\t\tprint_event(NULL, NULL, NULL);\n}\nstatic void test_save(struct tep_record *record, int cpu)\n{\n}\n#endif\n\nstatic void free_filter_strings(struct filter_str *filter_str)\n{\n\tstruct filter_str *filter;\n\n\twhile (filter_str) {\n\t\tfilter = filter_str;\n\t\tfilter_str = filter->next;\n\t\tfree(filter->filter);\n\t\tfree(filter);\n\t}\n}\n\nstatic struct input_files *add_input(const char *file)\n{\n\tstruct input_files *item;\n\n\titem = calloc(1, sizeof(*item));\n\tif (!item)\n\t\tdie(\"Failed to allocate for %s\", file);\n\titem->file = file;\n\titem->filter_str_next = &item->filter_str;\n\tlist_add_tail(&item->list, &input_files);\n\tlast_input_file = item;\n\treturn item;\n}\n\nstatic void add_handle(struct tracecmd_input *handle, struct input_files *input_files)\n{\n\tstruct handle_list *item;\n\tconst char *file = input_files ? input_files->file : input_file;\n\n\titem = calloc(1, sizeof(*item));\n\tif (!item)\n\t\tdie(\"Failed ot allocate for %s\", file);\n\titem->handle = handle;\n\tif (input_files) {\n\t\titem->file = file + strlen(file);\n\t\t/* we want just the base name */\n\t\twhile (item->file >= file && *item->file != '/')\n\t\t\titem->file--;\n\t\titem->file++;\n\t\tif (strlen(item->file) > max_file_size)\n\t\t\tmax_file_size = strlen(item->file);\n\n\t\titem->input_file = input_files;\n\t}\n\tlist_add_tail(&item->list, &handle_list);\n}\n\nstatic void free_inputs(void)\n{\n\tstruct input_files *item;\n\n\twhile (!list_empty(&input_files)) {\n\t\titem = container_of(input_files.next, struct input_files, list);\n\t\tlist_del(&item->list);\n\t\tfree_filter_strings(item->filter_str);\n\t\tfree(item);\n\t}\n}\n\nstatic void free_handles(void)\n{\n\tstruct handle_list *item;\n\n\twhile (!list_empty(&handle_list)) {\n\t\titem = container_of(handle_list.next, struct handle_list, list);\n\t\tlist_del(&item->list);\n\t\tfree(item);\n\t}\n}\n\nstatic void add_filter(struct input_files *input_file, const char *filter, int neg)\n{\n\tstruct filter_str *ftr;\n\n\tftr = malloc(sizeof(*ftr));\n\tif (!ftr)\n\t\tdie(\"Failed to allocate for filter %s\", filter);\n\tftr->filter = strdup(filter);\n\tif (!ftr->filter)\n\t\tdie(\"malloc\");\n\tftr->next = NULL;\n\tftr->neg = neg;\n\n\t/* must maintain order of command line */\n\tif (input_file) {\n\t\t*input_file->filter_str_next = ftr;\n\t\tinput_file->filter_str_next = &ftr->next;\n\t} else {\n\t\t*filter_next = ftr;\n\t\tfilter_next = &ftr->next;\n\t}\n}\n\nstatic void __add_filter(struct pid_list **head, const char *arg)\n{\n\tstruct pid_list *list;\n\tchar *pids = strdup(arg);\n\tchar *pid;\n\tchar *sav;\n\tint free = 1;\n\n\tif (!pids)\n\t\tdie(\"malloc\");\n\n\tpid = strtok_r(pids, \",\", &sav);\n\twhile (pid) {\n\t\tlist = malloc(sizeof(*list));\n\t\tif (!list)\n\t\t\tdie(\"Failed to allocate for arg %s\", arg);\n\t\tlist->pid = pid;\n\t\tlist->free = free;\n\t\tlist->next = *head;\n\t\t*head = list;\n\t\t/* The first pid needs to be freed */\n\t\tfree = 0;\n\t\tpid = strtok_r(NULL, \",\", &sav);\n\t}\n}\n\nstatic void add_comm_filter(const char *arg)\n{\n\t__add_filter(&comm_list, arg);\n}\n\nstatic void add_pid_filter(const char *arg)\n{\n\t__add_filter(&pid_list, arg);\n}\n\nstatic char *append_pid_filter(char *curr_filter, char *pid)\n{\n\tchar *filter;\n\tint len, curr_len;\n\n#define FILTER_FMT \"(common_pid==\" __STR \")||(pid==\" __STR \")||(next_pid==\" __STR \")\"\n\n#undef __STR\n#define __STR \"\"\n\n\t/* strlen(\".*:\") > strlen(\"||\") */\n\tlen = strlen(\".*:\" FILTER_FMT) + strlen(pid) * 3 + 1;\n\n#undef __STR\n#define __STR \"%s\"\n\n\tif (!curr_filter) {\n\t\tfilter = malloc(len);\n\t\tif (!filter)\n\t\t\tdie(\"Failed to allocate pid filter\");\n\t\tsprintf(filter, \".*:\" FILTER_FMT, pid, pid, pid);\n\t} else {\n\t\tcurr_len = strlen(curr_filter);\n\t\tlen += curr_len;\n\n\t\tfilter = realloc(curr_filter, len);\n\t\tif (!filter)\n\t\t\tdie(\"realloc\");\n\t\tsprintf(filter + curr_len, \"||\" FILTER_FMT, pid, pid, pid);\n\t}\n\n\treturn filter;\n}\n\nstatic void convert_comm_filter(struct tracecmd_input *handle)\n{\n\tstruct tep_cmdline *cmdline;\n\tstruct tep_handle *pevent;\n\tstruct pid_list *list;\n\n\tchar pidstr[100];\n\n\tif (!comm_list)\n\t\treturn;\n\n\tpevent = tracecmd_get_tep(handle);\n\n\t/* Seach for comm names and get their pids */\n\tfor (list = comm_list; list; list = list->next) {\n\t\tcmdline = tep_data_pid_from_comm(pevent, list->pid, NULL);\n\t\tif (!cmdline) {\n\t\t\twarning(\"comm: %s not in cmdline list\", list->pid);\n\t\t\tcontinue;\n\t\t}\n\t\tdo {\n\t\t\tsprintf(pidstr, \"%d\", tep_cmdline_pid(pevent, cmdline));\n\t\t\tadd_pid_filter(pidstr);\n\t\t\tcmdline = tep_data_pid_from_comm(pevent, list->pid,\n\t\t\t\t\t\t\t    cmdline);\n\t\t} while (cmdline);\n\t}\n\n\twhile (comm_list) {\n\t\tlist = comm_list;\n\t\tcomm_list = comm_list->next;\n\t\tif (list->free)\n\t\t\tfree(list->pid);\n\t\tfree(list);\n\t}\n}\n\nstatic void make_pid_filter(struct tracecmd_input *handle,\n\t\t\t    struct input_files *input_files)\n{\n\tstruct pid_list *list;\n\tchar *str = NULL;\n\n\tconvert_comm_filter(handle);\n\n\tif (!pid_list)\n\t\treturn;\n\n\t/* First do all common pids */\n\tfor (list = pid_list; list; list = list->next) {\n\t\tstr = append_pid_filter(str, list->pid);\n\t}\n\n\tadd_filter(input_files, str, 0);\n\tfree(str);\n\n\twhile (pid_list) {\n\t\tlist = pid_list;\n\t\tpid_list = pid_list->next;\n\t\tif (list->free)\n\t\t\tfree(list->pid);\n\t\tfree(list);\n\t}\n}\n\nstatic int __process_filters(struct tracecmd_input *handle,\n\t\t\t     struct filter_str *filters)\n{\n\tstruct tracecmd_filter *trace_filter;\n\n\tfor (; filters; filters = filters->next) {\n\t\ttrace_filter = tracecmd_filter_add(handle,\n\t\t\t\t\t\t   filters->filter,\n\t\t\t\t\t\t   filters->neg);\n\t\tif (!trace_filter)\n\t\t\tdie(\"Failed to create event filter: %s\",\n\t\t\t    filters->filter);\n\t}\n\n\treturn !!filters;\n}\n\nstatic void process_filters(struct handle_list *handles)\n{\n\tstruct input_files *input_file = handles->input_file ?: last_input_file;\n\tint added = 0;\n\n\tmake_pid_filter(handles->handle, input_file);\n\n\t/*\n\t * Order of filter processing matters. Apply the global filters\n\t * before file-specific ones.\n\t */\n\tadded += __process_filters(handles->handle, filter_strings);\n\tif (input_file)\n\t\tadded += __process_filters(handles->handle,\n\t\t\t\t\t   input_file->filter_str);\n\n\tif (added && test_filters_mode)\n\t\texit(0);\n}\n\nstatic void init_wakeup(struct tracecmd_input *handle)\n{\n\tstruct tep_handle *pevent;\n\tstruct tep_event *event;\n\n\tif (!show_wakeup)\n\t\treturn;\n\n\tpevent = tracecmd_get_tep(handle);\n\n\ttcmd_hash_init(&wakeup_hash, WAKEUP_HASH_SIZE);\n\n\tevent = tep_find_event_by_name(pevent, \"sched\", \"sched_wakeup\");\n\tif (!event)\n\t\tgoto fail;\n\twakeup_id = event->id;\n\twakeup_task = tep_find_field(event, \"pid\");\n\tif (!wakeup_task)\n\t\tgoto fail;\n\twakeup_success = tep_find_field(event, \"success\");\n\n\tevent = tep_find_event_by_name(pevent, \"sched\", \"sched_switch\");\n\tif (!event)\n\t\tgoto fail;\n\tsched_id = event->id;\n\tsched_task = tep_find_field(event, \"next_pid\");\n\tif (!sched_task)\n\t\tgoto fail;\n\n\tsched_prio = tep_find_field(event, \"next_prio\");\n\tif (!sched_prio)\n\t\tgoto fail;\n\n\n\twakeup_new_id = -1;\n\n\tevent = tep_find_event_by_name(pevent, \"sched\", \"sched_wakeup_new\");\n\tif (!event)\n\t\tgoto skip;\n\twakeup_new_id = event->id;\n\twakeup_new_task = tep_find_field(event, \"pid\");\n\tif (!wakeup_new_task)\n\t\tgoto fail;\n\twakeup_new_success = tep_find_field(event, \"success\");\n\n skip:\n\treturn;\n\n fail:\n\tshow_wakeup = 0;\n}\n\nstatic void add_wakeup(unsigned int val, unsigned long long start)\n{\n\tunsigned int key = trace_hash(val);\n\tstruct wakeup_info *info;\n\tstruct trace_hash_item *item;\n\n\titem = tcmd_hash_find(&wakeup_hash, key, NULL, NULL);\n\tif (item) {\n\t\tinfo = container_of(item, struct wakeup_info, hash);\n\t\t/* Hmm, double wakeup? */\n\t\tinfo->start = start;\n\t\treturn;\n\t}\n\n\tinfo = malloc(sizeof(*info));\n\tif (!info)\n\t\tdie(\"Failed to allocate wakeup info\");\n\tinfo->hash.key = key;\n\tinfo->start = start;\n\ttcmd_hash_add(&wakeup_hash, &info->hash);\n}\n\nstatic unsigned long long max_lat = 0;\nstatic unsigned long long max_time;\nstatic unsigned long long min_lat = -1;\nstatic unsigned long long min_time;\n\nstatic unsigned long long max_rt_lat = 0;\nstatic unsigned long long max_rt_time;\nstatic unsigned long long min_rt_lat = -1;\nstatic unsigned long long min_rt_time;\n\nstatic void add_sched(unsigned int val, unsigned long long end, int rt)\n{\n\tstruct trace_hash_item *item;\n\tunsigned int key = trace_hash(val);\n\tstruct wakeup_info *info;\n\tunsigned long long cal;\n\n\titem = tcmd_hash_find(&wakeup_hash, key, NULL, NULL);\n\tif (!item)\n\t\treturn;\n\n\tinfo = container_of(item, struct wakeup_info, hash);\n\n\tcal = end - info->start;\n\n\tif (cal > max_lat) {\n\t\tmax_lat = cal;\n\t\tmax_time = end;\n\t}\n\tif (cal < min_lat) {\n\t\tmin_lat = cal;\n\t\tmin_time = end;\n\t}\n\n\tif (rt) {\n\t\tif (cal > max_rt_lat) {\n\t\t\tmax_rt_lat = cal;\n\t\t\tmax_rt_time = end;\n\t\t}\n\t\tif (cal < min_rt_lat) {\n\t\t\tmin_rt_lat = cal;\n\t\t\tmin_rt_time = end;\n\t\t}\n\t}\n\n\tprintf(\" Latency: %llu.%03llu usecs\", cal / 1000, cal % 1000);\n\n\ttotal_wakeup_lat += cal;\n\twakeup_lat_count++;\n\n\tif (rt) {\n\t\ttotal_wakeup_rt_lat += cal;\n\t\twakeup_rt_lat_count++;\n\t}\n\n\ttrace_hash_del(item);\n\tfree(info);\n}\n\nstatic void process_wakeup(struct tep_handle *pevent, struct tep_record *record)\n{\n\tunsigned long long val;\n\tint id;\n\n\tif (!show_wakeup)\n\t\treturn;\n\n\tid = tep_data_type(pevent, record);\n\tif (id == wakeup_id) {\n\t\tif (tep_read_number_field(wakeup_success, record->data, &val) == 0) {\n\t\t\tif (!val)\n\t\t\t\treturn;\n\t\t}\n\t\tif (tep_read_number_field(wakeup_task, record->data, &val))\n\t\t\treturn;\n\t\tadd_wakeup(val, record->ts);\n\t} else if (id == wakeup_new_id) {\n\t\tif (tep_read_number_field(wakeup_new_success, record->data, &val) == 0) {\n\t\t\tif (!val)\n\t\t\t\treturn;\n\t\t}\n\t\tif (tep_read_number_field(wakeup_new_task, record->data, &val))\n\t\t\treturn;\n\t\tadd_wakeup(val, record->ts);\n\t} else if (id == sched_id) {\n\t\tint rt = 1;\n\t\tif (tep_read_number_field(sched_prio, record->data, &val))\n\t\t\treturn;\n\t\tif (val > 99)\n\t\t\trt = 0;\n\t\tif (tep_read_number_field(sched_task, record->data, &val))\n\t\t\treturn;\n\t\tadd_sched(val, record->ts, rt);\n\t}\n}\n\nstatic void\nshow_wakeup_timings(unsigned long long total, unsigned long count,\n\t\t    unsigned long long lat_max, unsigned long long time_max,\n\t\t    unsigned long long lat_min, unsigned long long time_min)\n{\n\n\ttotal /= count;\n\n\tprintf(\"\\nAverage wakeup latency: %llu.%03llu usecs\\n\",\n\t       total / 1000,\n\t       total % 1000);\n\tprintf(\"Maximum Latency: %llu.%03llu usecs at \", lat_max / 1000, lat_max % 1000);\n\tprintf(\"timestamp: %llu.%06llu\\n\",\n\t       time_max / 1000000000, ((time_max + 500) % 1000000000) / 1000);\n\tprintf(\"Minimum Latency: %llu.%03llu usecs at \", lat_min / 1000, lat_min % 1000);\n\tprintf(\"timestamp: %llu.%06llu\\n\\n\", time_min / 1000000000,\n\t       ((time_min + 500) % 1000000000) / 1000);\n}\n\nstatic void finish_wakeup(void)\n{\n\tstruct wakeup_info *info;\n\tstruct trace_hash_item **bucket;\n\tstruct trace_hash_item *item;\n\n\tif (!show_wakeup || !wakeup_lat_count)\n\t\treturn;\n\n\tshow_wakeup_timings(total_wakeup_lat, wakeup_lat_count,\n\t\t\t    max_lat, max_time,\n\t\t\t    min_lat, min_time);\n\n\n\tif (wakeup_rt_lat_count) {\n\t\tprintf(\"RT task timings:\\n\");\n\t\tshow_wakeup_timings(total_wakeup_rt_lat, wakeup_rt_lat_count,\n\t\t\t\t    max_rt_lat, max_rt_time,\n\t\t\t\t    min_rt_lat, min_rt_time);\n\t}\n\n\ttrace_hash_for_each_bucket(bucket, &wakeup_hash) {\n\t\ttrace_hash_while_item(item, bucket) {\n\t\t\ttrace_hash_del(item);\n\t\t\tinfo = container_of(item, struct wakeup_info, hash);\n\t\t\tfree(info);\n\t\t}\n\t}\n\n\ttcmd_hash_free(&wakeup_hash);\n}\n\nvoid trace_show_data(struct tracecmd_input *handle, struct tep_record *record)\n{\n\ttracecmd_show_data_func func = tracecmd_get_show_data_func(handle);\n\tconst char *tfmt = time_format(handle, TIME_FMT_NORMAL);\n\tconst char *cfmt = latency_format ? \"%8.8s-%-5d %3d\" : \"%16s-%-5d [%03d]\";\n\tstruct tep_handle *pevent;\n\tstruct tep_event *event;\n\tstruct trace_seq s;\n\tint cpu = record->cpu;\n\tbool use_trace_clock;\n\tstatic unsigned long long last_ts;\n\tunsigned long long diff_ts;\n\tunsigned long page_size;\n\tchar buf[50];\n\n\tpage_size = tracecmd_page_size(handle);\n\n\ttest_save(record, cpu);\n\n\tif (func) {\n\t\tfunc(handle, record);\n\t\treturn;\n\t}\n\n\tpevent = tracecmd_get_tep(handle);\n\tevent = tep_find_event_by_record(pevent, record);\n\tuse_trace_clock = tracecmd_get_use_trace_clock(handle);\n\n\ttrace_seq_init(&s);\n\tif (record->missed_events > 0)\n\t\ttrace_seq_printf(&s, \"CPU:%d [%lld EVENTS DROPPED]\\n\",\n\t\t\t\t cpu, record->missed_events);\n\telse if (record->missed_events < 0)\n\t\ttrace_seq_printf(&s, \"CPU:%d [EVENTS DROPPED]\\n\", cpu);\n\tif (buffer_breaks || tracecmd_get_debug()) {\n\t\tif (tracecmd_record_at_buffer_start(handle, record)) {\n\t\t\ttrace_seq_printf(&s, \"CPU:%d [SUBBUFFER START]\", cpu);\n\t\t\tif (tracecmd_get_debug())\n\t\t\t\ttrace_seq_printf(&s, \" [%lld:0x%llx]\",\n\t\t\t\t\t\t tracecmd_page_ts(handle, record),\n\t\t\t\t\t\t record->offset & ~(page_size - 1));\n\t\t\ttrace_seq_putc(&s, '\\n');\n\t\t}\n\t}\n\n\ttep_print_event(pevent, &s, record, cfmt,\n\t\t\tTEP_PRINT_COMM,\n\t\t\tTEP_PRINT_PID,\n\t\t\tTEP_PRINT_CPU);\n\n\tif (raw_format)\n\t\ttrace_seq_printf(&s, \"-0x%x\",\n\t\t\t\t tep_data_flags(pevent, record));\n\telse\n\t\ttep_print_event(pevent, &s, record,\n\t\t\t\tlatency_format ? \"%s\" : \" %s\",\n\t\t\t\tTEP_PRINT_LATENCY);\n\n\ttep_print_event(pevent, &s, record, tfmt, TEP_PRINT_TIME);\n\n\tif (tsdiff) {\n\t\tunsigned long long rec_ts = record->ts;\n\n\t\tbuf[0] = 0;\n\t\tif (use_trace_clock && !tep_test_flag(pevent, TEP_NSEC_OUTPUT))\n\t\t\trec_ts = (rec_ts + 500) / 1000;\n\t\tif (last_ts) {\n\t\t\tdiff_ts = rec_ts - last_ts;\n\t\t\tsnprintf(buf, 50, \"(+%lld)\", diff_ts);\n\t\t\tbuf[49] = 0;\n\t\t}\n\t\tlast_ts = rec_ts;\n\t\ttrace_seq_printf(&s, \" %-8s\", buf);\n\t}\n\n\tprint_event_name(&s, event);\n\ttep_print_event(pevent, &s, record, \"%s\", format_type);\n\n\tif (s.len && *(s.buffer + s.len - 1) == '\\n')\n\t\ts.len--;\n\tif (tracecmd_get_debug()) {\n\t\tstruct kbuffer *kbuf;\n\t\tstruct kbuffer_raw_info info;\n\t\tvoid *page;\n\t\tvoid *offset;\n\n\t\ttrace_seq_printf(&s, \" [%d:0x%llx:%d]\",\n\t\t\t\t tracecmd_record_ts_delta(handle, record),\n\t\t\t\t record->offset & (page_size - 1), record->size);\n\t\tkbuf = tracecmd_record_kbuf(handle, record);\n\t\tpage = tracecmd_record_page(handle, record);\n\t\toffset = tracecmd_record_offset(handle, record);\n\n\t\tif (kbuf && page && offset) {\n\t\t\tstruct kbuffer_raw_info *pi = &info;\n\n\t\t\t/* We need to get the record raw data to get next */\n\t\t\tpi->next = offset;\n\t\t\tpi = kbuffer_raw_get(kbuf, page, pi);\n\t\t\twhile ((pi = kbuffer_raw_get(kbuf, page, pi))) {\n\t\t\t\tif (pi->type < KBUFFER_TYPE_PADDING)\n\t\t\t\t\tbreak;\n\t\t\t\tswitch (pi->type) {\n\t\t\t\tcase KBUFFER_TYPE_PADDING:\n\t\t\t\t\ttrace_seq_printf(&s, \"\\n PADDING: \");\n\t\t\t\t\tbreak;\n\t\t\t\tcase KBUFFER_TYPE_TIME_EXTEND:\n\t\t\t\t\ttrace_seq_printf(&s, \"\\n TIME EXTEND: \");\n\t\t\t\t\tbreak;\n\t\t\t\tcase KBUFFER_TYPE_TIME_STAMP:\n\t\t\t\t\ttrace_seq_printf(&s, \"\\n TIME STAMP: \");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (pi->type == KBUFFER_TYPE_TIME_STAMP)\n\t\t\t\t\ttrace_seq_printf(&s, \"timestamp:%lld length:%d\",\n\t\t\t\t\t\t\t pi->delta,\n\t\t\t\t\t\t\t pi->length);\n\t\t\t\telse\n\t\t\t\t\ttrace_seq_printf(&s, \"delta:%lld length:%d\",\n\t\t\t\t\t\t\t pi->delta,\n\t\t\t\t\t\t\t pi->length);\n\t\t\t}\n\t\t}\n\t}\n\n\ttrace_seq_do_printf(&s);\n\ttrace_seq_destroy(&s);\n\n\tprocess_wakeup(pevent, record);\n\n\tprintf(\"\\n\");\n}\n\nstatic void read_latency(struct tracecmd_input *handle)\n{\n\tchar *buf = NULL;\n\tsize_t size = 0;\n\tint r;\n\n\tdo {\n\t\tr = tracecmd_latency_data_read(handle, &buf, &size);\n\t\tif (r > 0)\n\t\t\tprintf(\"%.*s\", r, buf);\n\t} while (r > 0);\n\n\tprintf(\"\\n\");\n\tfree(buf);\n}\n\nstatic int\ntest_filters(struct tep_handle *pevent, struct tep_record *record)\n{\n\tint ret = FILTER_NONE;\n\tint flags;\n\n\tif (no_irqs || no_softirqs) {\n\t\tflags = tep_data_flags(pevent, record);\n\t\tif (no_irqs && (flags & TRACE_FLAG_HARDIRQ))\n\t\t\treturn FILTER_MISS;\n\t\tif (no_softirqs && (flags & TRACE_FLAG_SOFTIRQ))\n\t\t\treturn FILTER_MISS;\n\t}\n\n\treturn ret;\n}\n\nstruct stack_info_cpu {\n\tint\t\t\tcpu;\n\tint\t\t\tlast_printed;\n};\n\nstruct stack_info {\n\tstruct stack_info\t*next;\n\tstruct handle_list\t*handles;\n\tstruct stack_info_cpu\t*cpus;\n\tint\t\t\tnr_cpus;\n};\n\nstatic void print_handle_file(struct handle_list *handles)\n{\n\t/* Only print file names if more than one file is read */\n\tif (!multi_inputs && !instances)\n\t\treturn;\n\tif (handles->file && *handles->file != '\\0')\n\t\tprintf(\"%*s: \", max_file_size, handles->file);\n\telse\n\t\tprintf(\"%*s  \", max_file_size, \"\");\n}\n\nstatic bool skip_record(struct handle_list *handles, struct tep_record *record, int cpu)\n{\n\tstruct tep_handle *tep;\n\tbool found = false;\n\tint ret;\n\n\ttep = tracecmd_get_tep(handles->handle);\n\n\tif (filter_cpus) {\n\t\tint i;\n\n\t\tfor (i = 0; filter_cpus[i] >= 0; i++) {\n\t\t\tif (filter_cpus[i] == cpu) {\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!found)\n\t\t\treturn true;\n\t\tfound = false;\n\t}\n\n\tret = test_filters(tep, record);\n\tswitch (ret) {\n\tcase FILTER_NOEXIST:\n\t\tbreak;\n\tcase FILTER_NONE:\n\tcase FILTER_MATCH:\n\t\t/* Test the negative filters (-v) */\n\t\tret = test_filters(tep, record);\n\t\tif (ret != FILTER_MATCH) {\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn !found;\n}\n\nstruct kvm_cpu_map {\n\tstruct tracecmd_input\t\t*guest_handle;\n\tint\t\t\t\tguest_vcpu;\n\tint\t\t\t\thost_pid;\n};\n\nstatic struct kvm_cpu_map *vcpu_maps;\nstatic int nr_vcpu_maps;\n\nstatic int cmp_map(const void *A, const void *B)\n{\n\tconst struct kvm_cpu_map *a = A;\n\tconst struct kvm_cpu_map *b = B;\n\n\tif (a->host_pid < b->host_pid)\n\t\treturn -1;\n\treturn a->host_pid > b->host_pid;\n}\n\nstatic void map_vcpus(struct tracecmd_input **handles, int nr_handles)\n{\n\tstruct tracecmd_input *host_handle = handles[0];\n\tunsigned long long traceid;\n\tstruct kvm_cpu_map *map;\n\tconst int *cpu_pids;\n\tconst char *name;\n\tint vcpu_count;\n\tint ret;\n\tint i, k;\n\n\tfor (i = 1; i < nr_handles; i++) {\n\t\ttraceid = tracecmd_get_traceid(handles[i]);\n\t\tret = tracecmd_get_guest_cpumap(host_handle, traceid,\n\t\t\t\t\t\t&name, &vcpu_count, &cpu_pids);\n\t\tif (ret)\n\t\t\tcontinue;\n\t\tmap = realloc(vcpu_maps, sizeof(*map) * (nr_vcpu_maps + vcpu_count));\n\t\tif (!map)\n\t\t\tdie(\"Could not allocate vcpu maps\");\n\n\t\tvcpu_maps = map;\n\t\tmap += nr_vcpu_maps;\n\t\tnr_vcpu_maps += vcpu_count;\n\n\t\tfor (k = 0; k < vcpu_count; k++) {\n\t\t\tmap[k].guest_handle = handles[i];\n\t\t\tmap[k].guest_vcpu = k;\n\t\t\tmap[k].host_pid = cpu_pids[k];\n\t\t}\n\t}\n\tif (!vcpu_maps)\n\t\treturn;\n\n\tqsort(vcpu_maps, nr_vcpu_maps, sizeof(*map), cmp_map);\n}\n\n\nconst char *tep_plugin_kvm_get_func(struct tep_event *event,\n\t\t\t\t    struct tep_record *record,\n\t\t\t\t    unsigned long long *val)\n{\n\tstruct tep_handle *tep;\n\tstruct kvm_cpu_map *map;\n\tstruct kvm_cpu_map key;\n\tunsigned long long rip = *val;\n\tconst char *func;\n\tint pid;\n\n\tif (!vcpu_maps || !nr_vcpu_maps)\n\t\treturn NULL;\n\n\t/*\n\t * A kvm event is referencing an address of the guest.\n\t * get the PID of this event, and then find which guest\n\t * it belongs to. Then return the function name from that guest's\n\t * handle.\n\t */\n\tpid = tep_data_pid(event->tep, record);\n\n\tkey.host_pid = pid;\n\tmap = bsearch(&key, vcpu_maps, nr_vcpu_maps, sizeof(*vcpu_maps), cmp_map);\n\n\tif (!map)\n\t\treturn NULL;\n\n\ttep = tracecmd_get_tep(map->guest_handle);\n\tfunc = tep_find_function(tep, rip);\n\tif (func)\n\t\t*val = tep_find_function_address(tep, rip);\n\treturn func;\n}\n\nstatic int process_record(struct tracecmd_input *handle, struct tep_record *record,\n\t\t\t  int cpu, void *data)\n{\n\tstruct handle_list *handles = tracecmd_get_private(handle);\n\tunsigned long long *last_timestamp = data;\n\n\tif (skip_record(handles, record, cpu))\n\t\treturn 0;\n\n\tif (tscheck && *last_timestamp > record->ts) {\n\t\terrno = 0;\n\t\twarning(\"WARNING: Record on cpu %d went backwards: %lld to %lld delta: -%lld\\n\",\n\t\t\tcpu, *last_timestamp, record->ts, *last_timestamp - record->ts);\n\t}\n\t*last_timestamp = record->ts;\n\n\tprint_handle_file(handles);\n\ttrace_show_data(handle, record);\n\treturn 0;\n}\n\nenum output_type {\n\tOUTPUT_NORMAL,\n\tOUTPUT_STAT_ONLY,\n\tOUTPUT_UNAME_ONLY,\n\tOUTPUT_VERSION_ONLY,\n};\n\nstatic void read_data_info(struct list_head *handle_list, enum output_type otype,\n\t\t\t   int global, int align_ts)\n{\n\tunsigned long long ts, first_ts;\n\tstruct handle_list *handles;\n\tstruct tracecmd_input **handle_array;\n\tunsigned long long last_timestamp = 0;\n\tint nr_handles = 0;\n\tint first = 1;\n\tint ret;\n\n\tlist_for_each_entry(handles, handle_list, list) {\n\t\tint cpus;\n\n\t\tnr_handles++;\n\n\t\tif (!tracecmd_is_buffer_instance(handles->handle)) {\n\t\t\tret = tracecmd_init_data(handles->handle);\n\t\t\tif (ret < 0)\n\t\t\t\tdie(\"failed to init data\");\n\t\t}\n\t\tcpus = tracecmd_cpus(handles->handle);\n\t\thandles->cpus = cpus;\n\n\t\tprocess_filters(handles);\n\n\t\t/* Don't process instances that we added here */\n\t\tif (tracecmd_is_buffer_instance(handles->handle))\n\t\t\tcontinue;\n\n\t\tif (align_ts) {\n\t\t\tts = tracecmd_get_first_ts(handles->handle);\n\t\t\tif (first || first_ts > ts)\n\t\t\t\tfirst_ts = ts;\n\t\t\tfirst = 0;\n\t\t}\n\t\tprint_handle_file(handles);\n\t\tprintf(\"cpus=%d\\n\", cpus);\n\n\t\t/* Latency trace is just all ASCII */\n\t\tif (ret > 0) {\n\t\t\tif (multi_inputs)\n\t\t\t\tdie(\"latency traces do not work with multiple inputs\");\n\t\t\tread_latency(handles->handle);\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (otype) {\n\t\tcase OUTPUT_NORMAL:\n\t\t\tbreak;\n\t\tcase OUTPUT_STAT_ONLY:\n\t\t\tprintf(\"\\nKernel buffer statistics:\\n\"\n\t\t\t       \"  Note: \\\"entries\\\" are the entries left in the kernel ring buffer and are not\\n\"\n\t\t\t       \"        recorded in the trace data. They should all be zero.\\n\\n\");\n\t\t\ttracecmd_print_stats(handles->handle);\n\t\t\tcontinue;\n\t\tcase OUTPUT_UNAME_ONLY:\n\t\t\ttracecmd_print_uname(handles->handle);\n\t\tcase OUTPUT_VERSION_ONLY:\n\t\t\ttracecmd_print_version(handles->handle);\n\t\t\tcontinue;\n\t\t}\n\n\t\tinit_wakeup(handles->handle);\n\t\tif (last_hook)\n\t\t\tlast_hook->next = tracecmd_hooks(handles->handle);\n\t\telse\n\t\t\thooks = tracecmd_hooks(handles->handle);\n\t\tif (profile)\n\t\t\ttrace_init_profile(handles->handle, hooks, global);\n\n\t\t/* If this file has buffer instances, get the handles for them */\n\t\tinstances = tracecmd_buffer_instances(handles->handle);\n\t\tif (instances) {\n\t\t\tstruct tracecmd_input *new_handle;\n\t\t\tstruct input_files *file_input;\n\t\t\tconst char *save_name;\n\t\t\tconst char *name;\n\t\t\tint i;\n\n\t\t\tfile_input = handles->input_file;\n\n\t\t\tfor (i = 0; i < instances; i++) {\n\t\t\t\tname = tracecmd_buffer_instance_name(handles->handle, i);\n\t\t\t\tif (!name)\n\t\t\t\t\tdie(\"error in reading buffer instance\");\n\t\t\t\tnew_handle = tracecmd_buffer_instance_handle(handles->handle, i);\n\t\t\t\tif (!new_handle) {\n\t\t\t\t\twarning(\"could not retrieve handle %s\", name);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (file_input) {\n\t\t\t\t\tsave_name = file_input->file;\n\t\t\t\t\tfile_input->file = name;\n\t\t\t\t} else {\n\t\t\t\t\tsave_name = NULL;\n\t\t\t\t\tfile_input = add_input(name);\n\t\t\t\t}\n\t\t\t\tadd_handle(new_handle, file_input);\n\t\t\t\tif (save_name)\n\t\t\t\t\tfile_input->file = save_name;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (otype != OUTPUT_NORMAL)\n\t\treturn;\n\n\tif (align_ts) {\n\t\tlist_for_each_entry(handles, handle_list, list) {\n\t\t\ttracecmd_add_ts_offset(handles->handle, -first_ts);\n\t\t}\n\t}\n\n\thandle_array = calloc(nr_handles, sizeof(*handle_array));\n\tif (!handle_array)\n\t\tdie(\"Could not allocate memory for handle list\");\n\n\tnr_handles = 0;\n\tlist_for_each_entry(handles, handle_list, list) {\n\t\ttracecmd_set_private(handles->handle, handles);\n\t\thandle_array[nr_handles++] = handles->handle;\n\t}\n\n\tmap_vcpus(handle_array, nr_handles);\n\n\ttracecmd_iterate_events_multi(handle_array, nr_handles,\n\t\t\t\t      process_record, &last_timestamp);\n\n\tfree(handle_array);\n\n\tif (profile)\n\t\tdo_trace_profile();\n\n\tlist_for_each_entry(handles, handle_list, list) {\n\t\tshow_test(handles->handle);\n\t}\n}\n\nstruct tracecmd_input *read_trace_header(const char *file, int flags)\n{\n\tinput_fd = open(file, O_RDONLY);\n\tif (input_fd < 0)\n\t\tdie(\"opening '%s'\\n\", file);\n\n\treturn tracecmd_alloc_fd(input_fd, flags);\n}\n\nstatic void sig_end(int sig)\n{\n\tstruct handle_list *handles;\n\n\tfprintf(stderr, \"trace-cmd: Received SIGINT\\n\");\n\n\tlist_for_each_entry(handles, &handle_list, list) {\n\t\ttracecmd_close(handles->handle);\n\t}\n\n\texit(0);\n}\n\nstatic const char *skip_space_and_test_digit(const char *p, const char *cpu_str)\n{\n\twhile (isspace(*p))\n\t\tp++;\n\tif (!isdigit(*p))\n\t\tdie(\"invalid character '%c' in cpu string '%s'\",\n\t\t    *p, cpu_str);\n\treturn p;\n}\n\nstatic void __add_cpu(int cpu)\n{\n\tfilter_cpus = tracecmd_add_id(filter_cpus, cpu, nr_filter_cpus++);\n}\n\nstatic void parse_cpulist(const char *cpu_str)\n{\n\tunsigned a, b;\n\tconst char *s = cpu_str;\n\n\tdo {\n\t\ts = skip_space_and_test_digit(s, cpu_str);\n\t\tb = a = strtoul(s, (char **)&s, 10);\n\t\tif (*s == '-') {\n\t\t\ts = skip_space_and_test_digit(s + 1, cpu_str);\n\t\t\tb = strtoul(s, (char **)&s, 10);\n\t\t}\n\t\tif (!(a <= b))\n\t\t\tdie(\"range of cpu numbers must be lower to greater\");\n\t\twhile (a <= b) {\n\t\t\t__add_cpu(a);\n\t\t\ta++;\n\t\t}\n\t\tif (*s == ',' || *s == ':')\n\t\t\ts++;\n\t} while (*s != '\\0');\n}\n\nstatic void read_file_fd(int fd, char *dst, int len)\n{\n\tsize_t size = 0;\n\tint r;\n\n\tdo {\n\t\tr = read(fd, dst+size, len);\n\t\tif (r > 0) {\n\t\t\tsize += r;\n\t\t\tlen -= r;\n\t\t}\n\t} while (r > 0);\n}\n\nstatic void add_functions(struct tep_handle *pevent, const char *file)\n{\n\tstruct stat st;\n\tchar *buf;\n\tint ret;\n\tint fd;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0)\n\t\tdie(\"Can't read file %s\", file);\n\n\tret = fstat(fd, &st);\n\tif (ret < 0)\n\t\tdie(\"Can't stat file %s\", file);\n\n\tbuf = malloc(st.st_size + 1);\n\tif (!buf)\n\t\tdie(\"Failed to allocate for function buffer\");\n\tread_file_fd(fd, buf, st.st_size);\n\tbuf[st.st_size] = '\\0';\n\tclose(fd);\n\ttep_parse_kallsyms(pevent, buf);\n\tfree(buf);\n}\n\nstatic void process_plugin_option(char *option)\n{\n\tchar *name = option;\n\tchar *val = NULL;\n\tchar *p;\n\n\tif ((p = strstr(name, \"=\"))) {\n\t\t*p = '\\0';\n\t\tval = p+1;\n\t}\n\ttep_plugin_add_option(name, val);\n}\n\nstatic void set_event_flags(struct tep_handle *pevent, struct event_str *list,\n\t\t\t    unsigned int flag)\n{\n\tstruct tep_event **events;\n\tstruct tep_event *event;\n\tstruct event_str *str;\n\tregex_t regex;\n\tint ret;\n\tint i;\n\n\tif (!list)\n\t\treturn;\n\n\tevents = tep_list_events(pevent, 0);\n\n\tfor (str = list; str; str = str->next) {\n\t\tchar *match;\n\n\t\tmatch = malloc(strlen(str->event) + 3);\n\t\tif (!match)\n\t\t\tdie(\"Failed to allocate for match string '%s'\", str->event);\n\t\tsprintf(match, \"^%s$\", str->event);\n\n\t\tret = regcomp(&regex, match, REG_ICASE|REG_NOSUB);\n\t\tif (ret < 0)\n\t\t\tdie(\"Can't parse '%s'\", str->event);\n\t\tfree(match);\n\t\tfor (i = 0; events[i]; i++) {\n\t\t\tevent = events[i];\n\t\t\tif (!regexec(&regex, event->name, 0, NULL, 0) ||\n\t\t\t    !regexec(&regex, event->system, 0, NULL, 0))\n\t\t\t\tevent->flags |= flag;\n\t\t}\n\t}\n}\n\nstatic void show_event_ts(struct tracecmd_input *handle,\n\t\t\t  struct tep_record *record)\n{\n\tconst char *tfmt = time_format(handle, TIME_FMT_TS);\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tstruct trace_seq s;\n\n\ttrace_seq_init(&s);\n\ttep_print_event(tep, &s, record, tfmt, TEP_PRINT_TIME);\n\tprintf(\"%s\", s.buffer);\n\ttrace_seq_destroy(&s);\n}\n\nstatic void add_hook(const char *arg)\n{\n\tstruct hook_list *hook;\n\n\thook = tracecmd_create_event_hook(arg);\n\n\thook->next = hooks;\n\thooks = hook;\n\tif (!last_hook)\n\t\tlast_hook = hook;\n}\n\nstatic void add_first_input(const char *input_file, long long tsoffset)\n{\n\tstruct input_files *item;\n\n\t/* Copy filter strings to this input file */\n\titem = add_input(input_file);\n\titem->filter_str = filter_strings;\n\tif (filter_strings)\n\t\titem->filter_str_next = filter_next;\n\telse\n\t\titem->filter_str_next = &item->filter_str;\n\t/* Copy the tsoffset to this input file */\n\titem->tsoffset = tsoffset;\n}\n\nenum {\n\tOPT_verbose\t= 234,\n\tOPT_align_ts\t= 235,\n\tOPT_raw_ts\t= 236,\n\tOPT_version\t= 237,\n\tOPT_tscheck\t= 238,\n\tOPT_tsdiff\t= 239,\n\tOPT_ts2secs\t= 240,\n\tOPT_tsoffset\t= 241,\n\tOPT_bycomm\t= 242,\n\tOPT_debug\t= 243,\n\tOPT_uname\t= 244,\n\tOPT_profile\t= 245,\n\tOPT_event\t= 246,\n\tOPT_comm\t= 247,\n\tOPT_boundary\t= 248,\n\tOPT_stat\t= 249,\n\tOPT_pid\t\t= 250,\n\tOPT_nodate\t= 251,\n\tOPT_check_event_parsing\t= 252,\n\tOPT_kallsyms\t= 253,\n\tOPT_events\t= 254,\n\tOPT_cpu\t\t= 255,\n\tOPT_cpus\t= 256,\n\tOPT_first\t= 257,\n\tOPT_last\t= 258,\n};\n\nvoid trace_report (int argc, char **argv)\n{\n\tstruct tracecmd_input *handle;\n\tstruct tep_handle *pevent;\n\tstruct event_str *raw_events = NULL;\n\tstruct event_str *nohandler_events = NULL;\n\tstruct event_str **raw_ptr = &raw_events;\n\tstruct event_str **nohandler_ptr = &nohandler_events;\n\tconst char *functions = NULL;\n\tconst char *print_event = NULL;\n\tstruct input_files *inputs;\n\tstruct handle_list *handles;\n\tenum output_type otype;\n\tlong long tsoffset = 0;\n\tunsigned long long ts2secs = 0;\n\tunsigned long long ts2sc;\n\tint open_flags = 0;\n\tint show_stat = 0;\n\tint show_funcs = 0;\n\tint show_endian = 0;\n\tint show_page_size = 0;\n\tint show_printk = 0;\n\tint show_uname = 0;\n\tint show_version = 0;\n\tint show_events = 0;\n\tint show_cpus = 0;\n\tint show_first = 0;\n\tint show_last = 0;\n\tint print_events = 0;\n\tint nanosec = 0;\n\tint no_date = 0;\n\tint raw_ts = 0;\n\tint align_ts = 0;\n\tint global = 0;\n\tint neg = 0;\n\tint ret = 0;\n\tint check_event_parsing = 0;\n\tint c;\n\n\tlist_head_init(&handle_list);\n\tlist_head_init(&input_files);\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"report\") != 0)\n\t\tusage(argv);\n\n\tsignal(SIGINT, sig_end);\n\n\ttrace_set_loglevel(TEP_LOG_ERROR);\n\n\tfor (;;) {\n\t\tint option_index = 0;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"cpu\", required_argument, NULL, OPT_cpu},\n\t\t\t{\"cpus\", no_argument, NULL, OPT_cpus},\n\t\t\t{\"events\", no_argument, NULL, OPT_events},\n\t\t\t{\"event\", required_argument, NULL, OPT_event},\n\t\t\t{\"filter-test\", no_argument, NULL, 'T'},\n\t\t\t{\"first-event\", no_argument, NULL, OPT_first},\n\t\t\t{\"kallsyms\", required_argument, NULL, OPT_kallsyms},\n\t\t\t{\"pid\", required_argument, NULL, OPT_pid},\n\t\t\t{\"comm\", required_argument, NULL, OPT_comm},\n\t\t\t{\"check-events\", no_argument, NULL,\n\t\t\t\tOPT_check_event_parsing},\n\t\t\t{\"nodate\", no_argument, NULL, OPT_nodate},\n\t\t\t{\"stat\", no_argument, NULL, OPT_stat},\n\t\t\t{\"boundary\", no_argument, NULL, OPT_boundary},\n\t\t\t{\"debug\", no_argument, NULL, OPT_debug},\n\t\t\t{\"last-event\", no_argument, NULL, OPT_last},\n\t\t\t{\"profile\", no_argument, NULL, OPT_profile},\n\t\t\t{\"uname\", no_argument, NULL, OPT_uname},\n\t\t\t{\"version\", no_argument, NULL, OPT_version},\n\t\t\t{\"by-comm\", no_argument, NULL, OPT_bycomm},\n\t\t\t{\"ts-offset\", required_argument, NULL, OPT_tsoffset},\n\t\t\t{\"ts2secs\", required_argument, NULL, OPT_ts2secs},\n\t\t\t{\"ts-diff\", no_argument, NULL, OPT_tsdiff},\n\t\t\t{\"ts-check\", no_argument, NULL, OPT_tscheck},\n\t\t\t{\"raw-ts\", no_argument, NULL, OPT_raw_ts},\n\t\t\t{\"align-ts\", no_argument, NULL, OPT_align_ts},\n\t\t\t{\"verbose\", optional_argument, NULL, OPT_verbose},\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long (argc-1, argv+1, \"+hSIi:H:feGpRr:tPNn:LlEwF:V::vTqO:\",\n\t\t\tlong_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\tif (input_file) {\n\t\t\t\tmulti_inputs++;\n\t\t\t\tadd_input(optarg);\n\t\t\t} else {\n\t\t\t\tinput_file = optarg;\n\t\t\t\tadd_first_input(input_file, tsoffset);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'F':\n\t\t\tadd_filter(last_input_file, optarg, neg);\n\t\t\tbreak;\n\t\tcase 'H':\n\t\t\tadd_hook(optarg);\n\t\t\tbreak;\n\t\tcase 'T':\n\t\t\ttest_filters_mode = 1;\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tshow_funcs = 1;\n\t\t\tbreak;\n\t\tcase 'I':\n\t\t\tno_irqs = 1;\n\t\t\tbreak;\n\t\tcase 'S':\n\t\t\tno_softirqs = 1;\n\t\t\tbreak;\n\t\tcase 'P':\n\t\t\tshow_printk = 1;\n\t\t\tbreak;\n\t\tcase 'L':\n\t\t\topen_flags |= TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS;\n\t\t\tbreak;\n\t\tcase 'N':\n\t\t\topen_flags |= TRACECMD_FL_LOAD_NO_PLUGINS;\n\t\t\tbreak;\n\t\tcase 'n':\n\t\t\t*nohandler_ptr = malloc(sizeof(struct event_str));\n\t\t\tif (!*nohandler_ptr)\n\t\t\t\tdie(\"Failed to allocate for '-n %s'\", optarg);\n\t\t\t(*nohandler_ptr)->event = optarg;\n\t\t\t(*nohandler_ptr)->next = NULL;\n\t\t\tnohandler_ptr = &(*nohandler_ptr)->next;\n\t\t\tbreak;\n\t\tcase 'e':\n\t\t\tshow_endian = 1;\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tshow_page_size = 1;\n\t\t\tbreak;\n\t\tcase 'E':\n\t\t\tshow_events = 1;\n\t\t\tbreak;\n\t\tcase 'G':\n\t\t\tglobal = 1;\n\t\t\tbreak;\n\t\tcase 'R':\n\t\t\traw_format = true;\n\t\t\tbreak;\n\t\tcase 'r':\n\t\t\t*raw_ptr = malloc(sizeof(struct event_str));\n\t\t\tif (!*raw_ptr)\n\t\t\t\tdie(\"Failed to allocate '-r %s'\", optarg);\n\t\t\t(*raw_ptr)->event = optarg;\n\t\t\t(*raw_ptr)->next = NULL;\n\t\t\traw_ptr = &(*raw_ptr)->next;\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\tnanosec = 1;\n\t\t\tbreak;\n\t\tcase 'w':\n\t\t\tshow_wakeup = 1;\n\t\t\tbreak;\n\t\tcase 'l':\n\t\t\tlatency_format = 1;\n\t\t\tbreak;\n\t\tcase 'O':\n\t\t\tprocess_plugin_option(optarg);\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\tif (neg)\n\t\t\t\tdie(\"Only 1 -v can be used\");\n\t\t\tneg = 1;\n\t\t\tbreak;\n\t\tcase 'q':\n\t\t\tsilence_warnings = 1;\n\t\t\ttracecmd_set_loglevel(TEP_LOG_NONE);\n\t\t\tbreak;\n\t\tcase OPT_cpu:\n\t\t\tparse_cpulist(optarg);\n\t\t\tbreak;\n\t\tcase OPT_cpus:\n\t\t\tshow_cpus = 1;\n\t\t\tbreak;\n\t\tcase OPT_events:\n\t\t\tprint_events = 1;\n\t\t\tbreak;\n\t\tcase OPT_event:\n\t\t\tprint_event = optarg;\n\t\t\tbreak;\n\t\tcase OPT_kallsyms:\n\t\t\tfunctions = optarg;\n\t\t\tbreak;\n\t\tcase OPT_pid:\n\t\t\tadd_pid_filter(optarg);\n\t\t\tbreak;\n\t\tcase OPT_comm:\n\t\t\tadd_comm_filter(optarg);\n\t\t\tbreak;\n\t\tcase OPT_check_event_parsing:\n\t\t\tcheck_event_parsing = 1;\n\t\t\tbreak;\n\t\tcase OPT_nodate:\n\t\t\tno_date = 1;\n\t\t\tbreak;\n\t\tcase OPT_stat:\n\t\t\tshow_stat = 1;\n\t\t\tbreak;\n\t\tcase OPT_first:\n\t\t\tshow_first = 1;\n\t\t\tshow_cpus = 1;\n\t\t\tbreak;\n\t\tcase OPT_last:\n\t\t\tshow_last = 1;\n\t\t\tshow_cpus = 1;\n\t\t\tbreak;\n\t\tcase OPT_boundary:\n\t\t\t/* Debug to look at buffer breaks */\n\t\t\tbuffer_breaks = 1;\n\t\t\tbreak;\n\t\tcase OPT_debug:\n\t\t\tbuffer_breaks = 1;\n\t\t\ttracecmd_set_debug(true);\n\t\t\tbreak;\n\t\tcase OPT_profile:\n\t\t\tprofile = 1;\n\t\t\tbreak;\n\t\tcase OPT_uname:\n\t\t\tshow_uname = 1;\n\t\t\tbreak;\n\t\tcase OPT_version:\n\t\t\tshow_version = 1;\n\t\t\tbreak;\n\t\tcase OPT_bycomm:\n\t\t\ttrace_profile_set_merge_like_comms();\n\t\t\tbreak;\n\t\tcase OPT_ts2secs:\n\t\t\tts2sc = atoll(optarg);\n\t\t\tif (multi_inputs)\n\t\t\t\tlast_input_file->ts2secs = ts2sc;\n\t\t\telse\n\t\t\t\tts2secs = ts2sc;\n\t\t\tbreak;\n\t\tcase OPT_tsoffset:\n\t\t\ttsoffset = atoll(optarg);\n\t\t\tif (multi_inputs)\n\t\t\t\tlast_input_file->tsoffset = tsoffset;\n\t\t\tif (!input_file)\n\t\t\t\tdie(\"--ts-offset must come after -i\");\n\t\t\tbreak;\n\t\tcase OPT_tsdiff:\n\t\t\ttsdiff = 1;\n\t\t\tbreak;\n\t\tcase OPT_tscheck:\n\t\t\ttscheck = 1;\n\t\t\tbreak;\n\t\tcase OPT_raw_ts:\n\t\t\traw_ts = 1;\n\t\t\tbreak;\n\t\tcase OPT_align_ts:\n\t\t\talign_ts = 1;\n\t\t\tbreak;\n\t\tcase 'V':\n\t\tcase OPT_verbose:\n\t\t\tshow_status = 1;\n\t\t\tif (trace_set_verbose(optarg) < 0)\n\t\t\t\tdie(\"invalid verbose level %s\", optarg);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif ((argc - optind) >= 2) {\n\t\tif (input_file)\n\t\t\tusage(argv);\n\t\tinput_file = argv[optind + 1];\n\t\tadd_first_input(input_file, tsoffset);\n\t\tfor (int i = optind + 2; i < argc; i++) {\n\t\t\tmulti_inputs++;\n\t\t\tadd_input(argv[i]);\n\t\t}\n\t}\n\n\tif (!multi_inputs) {\n\t\tif (!input_file) {\n\t\t\tinput_file = default_input_file;\n\t\t\tadd_first_input(input_file, tsoffset);\n\t\t}\n\t} else if (show_wakeup)\n\t\tdie(\"Wakeup tracing can only be done on a single input file\");\n\n\tlist_for_each_entry(inputs, &input_files, list) {\n\t\thandle = read_trace_header(inputs->file, open_flags);\n\t\tif (!handle)\n\t\t\tdie(\"error reading header for %s\", inputs->file);\n\n\t\t/* If used with instances, top instance will have no tag */\n\t\tadd_handle(handle, multi_inputs ? inputs : NULL);\n\n\t\tif (no_date)\n\t\t\ttracecmd_set_flag(handle, TRACECMD_FL_IGNORE_DATE);\n\t\tif (raw_ts)\n\t\t\ttracecmd_set_flag(handle, TRACECMD_FL_RAW_TS);\n\t\tpage_size = tracecmd_page_size(handle);\n\n\t\tif (show_page_size) {\n\t\t\tprintf(\"file page size is %d, and host page size is %d\\n\",\n\t\t\t       page_size,\n\t\t\t       getpagesize());\n\t\t\treturn;\n\t\t}\n\n\t\tif (inputs->tsoffset)\n\t\t\ttracecmd_set_ts_offset(handle, inputs->tsoffset);\n\n\t\tif (inputs->ts2secs)\n\t\t\ttracecmd_set_ts2secs(handle, inputs->ts2secs);\n\t\telse if (ts2secs)\n\t\t\ttracecmd_set_ts2secs(handle, ts2secs);\n\n\t\tpevent = tracecmd_get_tep(handle);\n\n\t\tif (nanosec)\n\t\t\ttep_set_flag(pevent, TEP_NSEC_OUTPUT);\n\n\t\tif (raw_format)\n\t\t\tformat_type = TEP_PRINT_INFO_RAW;\n\n\t\tif (test_filters_mode)\n\t\t\ttep_set_test_filters(pevent, 1);\n\n\t\tif (functions)\n\t\t\tadd_functions(pevent, functions);\n\n\t\tif (show_endian) {\n\t\t\tprintf(\"file is %s endian and host is %s endian\\n\",\n\t\t\t\ttep_is_file_bigendian(pevent) ? \"big\" : \"little\",\n\t\t\t\ttep_is_local_bigendian(pevent) ? \"big\" : \"little\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (print_events) {\n\t\t\ttracecmd_print_events(handle, NULL);\n\t\t\treturn;\n\t\t}\n\n\t\tif (print_event) {\n\t\t\ttracecmd_print_events(handle, print_event);\n\t\t\treturn;\n\t\t}\n\n\t\tret = tracecmd_read_headers(handle, 0);\n\t\tif (check_event_parsing) {\n\t\t\tif (ret || tracecmd_get_parsing_failures(handle))\n\t\t\t\texit(EINVAL);\n\t\t\telse\n\t\t\t\texit(0);\n\t\t} else {\n\t\t\tif (ret)\n\t\t\t\treturn;\n\t\t}\n\n\t\tif (show_funcs) {\n\t\t\ttep_print_funcs(pevent);\n\t\t\treturn;\n\t\t}\n\t\tif (show_printk) {\n\t\t\ttep_print_printk(pevent);\n\t\t\treturn;\n\t\t}\n\n\t\tif (show_events) {\n\t\t\tstruct tep_event **events;\n\t\t\tstruct tep_event *event;\n\t\t\tint i;\n\n\t\t\tevents = tep_list_events(pevent, TEP_EVENT_SORT_SYSTEM);\n\t\t\tfor (i = 0; events[i]; i++) {\n\t\t\t\tevent = events[i];\n\t\t\t\tif (event->system)\n\t\t\t\t\tprintf(\"%s:\", event->system);\n\t\t\t\tprintf(\"%s\\n\", event->name);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (show_cpus) {\n\t\t\tstruct tep_record *record;\n\t\t\tint cpus;\n\t\t\tint ret;\n\t\t\tint i;\n\n\t\t\tif (!tracecmd_is_buffer_instance(handle)) {\n\t\t\t\tret = tracecmd_init_data(handle);\n\t\t\t\tif (ret < 0)\n\t\t\t\t\tdie(\"failed to init data\");\n\t\t\t}\n\t\t\tcpus = tracecmd_cpus(handle);\n\t\t\tprintf(\"List of CPUs in %s with data:\\n\", inputs->file);\n\t\t\tfor (i = 0; i < cpus; i++) {\n\t\t\t\tif ((record = tracecmd_read_cpu_first(handle, i))) {\n\t\t\t\t\tprintf(\"  %d\", i);\n\t\t\t\t\tif (show_first) {\n\t\t\t\t\t\tprintf(\"\\tFirst event:\");\n\t\t\t\t\t\tshow_event_ts(handle, record);\n\t\t\t\t\t}\n\t\t\t\t\tif (show_last) {\n\t\t\t\t\t\ttracecmd_free_record(record);\n\t\t\t\t\t\trecord = tracecmd_read_cpu_last(handle, i);\n\t\t\t\t\t\tif (record) {\n\t\t\t\t\t\t\tprintf(\"\\tLast event:\");\n\t\t\t\t\t\t\tshow_event_ts(handle, record);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttracecmd_free_record(record);\n\t\t\t\t\tprintf(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tset_event_flags(pevent, nohandler_events, TEP_EVENT_FL_NOHANDLE);\n\t\tset_event_flags(pevent, raw_events, TEP_EVENT_FL_PRINTRAW);\n\t}\n\n\tif (show_cpus)\n\t\treturn;\n\n\totype = OUTPUT_NORMAL;\n\n\tif (tracecmd_get_flags(handle) & TRACECMD_FL_RAW_TS) {\n\t\ttep_func_repeat_format = \"%d\";\n\t} else if (tracecmd_get_flags(handle) & TRACECMD_FL_IN_USECS) {\n\t\tif (tep_test_flag(tracecmd_get_tep(handle), TEP_NSEC_OUTPUT))\n\t\t\ttep_func_repeat_format = \"%9.1d\";\n\t\telse\n\t\t\ttep_func_repeat_format = \"%6.1000d\";\n\t} else {\n\t\ttep_func_repeat_format = \"%12d\";\n\t}\n\n\n\tif (show_stat)\n\t\totype = OUTPUT_STAT_ONLY;\n\t/* yeah yeah, uname overrides stat */\n\tif (show_uname)\n\t\totype = OUTPUT_UNAME_ONLY;\n\t/* and version overrides uname! */\n\tif (show_version)\n\t\totype = OUTPUT_VERSION_ONLY;\n\tread_data_info(&handle_list, otype, global, align_ts);\n\n\tlist_for_each_entry(handles, &handle_list, list) {\n\t\ttracecmd_close(handles->handle);\n\t}\n\tfree_handles();\n\tfree_inputs();\n\n\tfinish_wakeup();\n\n\treturn;\n}\n"
  },
  {
    "path": "tracecmd/trace-record.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n */\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <getopt.h>\n#include <time.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/wait.h>\n#include <sys/socket.h>\n#include <sys/syscall.h>\n#include <sys/utsname.h>\n#ifndef NO_PTRACE\n#include <sys/ptrace.h>\n#else\n#ifdef WARN_NO_PTRACE\n#warning ptrace not supported. -c feature will not work\n#endif\n#endif\n#include <netdb.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <sched.h>\n#include <glob.h>\n#include <errno.h>\n#include <limits.h>\n#include <libgen.h>\n#include <poll.h>\n#include <pwd.h>\n#include <grp.h>\n\n#include \"tracefs.h\"\n#include \"version.h\"\n#include \"trace-local.h\"\n#include \"trace-msg.h\"\n\n#define _STR(x) #x\n#define STR(x) _STR(x)\n\n#define RECORD_PIDFILE\t\"trace-cmd-record.pid\"\n\n#define TRACE_CTRL\t\"tracing_on\"\n#define TRACE\t\t\"trace\"\n#define AVAILABLE\t\"available_tracers\"\n#define CURRENT\t\t\"current_tracer\"\n#define ITER_CTRL\t\"trace_options\"\n#define MAX_LATENCY\t\"tracing_max_latency\"\n#define STAMP\t\t\"stamp\"\n#define FUNC_STACK_TRACE \"func_stack_trace\"\n#define TSC_CLOCK\t\"x86-tsc\"\n\n#define dprint(fmt, ...)\ttracecmd_debug(fmt, ##__VA_ARGS__)\n\nenum trace_type {\n\tTRACE_TYPE_RECORD\t= 1,\n\tTRACE_TYPE_START\t= (1 << 1),\n\tTRACE_TYPE_STREAM\t= (1 << 2),\n\tTRACE_TYPE_EXTRACT\t= (1 << 3),\n\tTRACE_TYPE_SET\t\t= (1 << 4),\n};\n\nstatic tracecmd_handle_init_func handle_init = NULL;\n\nstatic int rt_prio;\n\nstatic int keep;\nstatic int Keep; // do not reset before tracing\n\nstatic int latency;\nstatic long sleep_time = 1000;\nstatic int recorder_threads;\nstatic struct pid_record_data *pids;\nstatic int buffers;\n\n/* Clear all function filters */\nstatic int clear_function_filters;\n\nstatic bool no_fifos;\n\nstatic char *host;\n\nstatic const char *gai_err;\n\nstatic bool quiet;\n\nstatic bool fork_process;\n\nstatic bool do_daemonize;\n\nstatic bool created_pidfile;\n\n/* Max size to let a per cpu file get */\nstatic int max_kb;\n\nstatic int do_ptrace;\n\nstatic int filter_task;\nstatic bool no_filter = false;\n\nstatic int local_cpu_count;\n\nstatic int finished;\n\n/* setting of /proc/sys/kernel/ftrace_enabled */\nstatic int fset;\n\nstatic unsigned recorder_flags;\n\n/* Try a few times to get an accurate date */\nstatic int date2ts_tries = 50;\n\nstatic struct func_list *graph_funcs;\n\nstatic int func_stack;\n\nstatic int save_stdout = -1;\n\nstatic struct hook_list *hooks;\n\nstruct event_list {\n\tstruct event_list *next;\n\tconst char *event;\n\tchar *trigger;\n\tchar *filter;\n\tchar *pid_filter;\n\tchar *filter_file;\n\tchar *trigger_file;\n\tchar *enable_file;\n\tint neg;\n};\n\nstruct tracecmd_event_list *listed_events;\n\nstruct events {\n\tstruct events *sibling;\n\tstruct events *children;\n\tstruct events *next;\n\tchar *name;\n};\n\n/* Files to be reset when done recording */\nstruct reset_file {\n\tstruct reset_file\t*next;\n\tchar\t\t\t*path;\n\tchar\t\t\t*reset;\n\tint\t\t\tprio;\n};\n\nstatic struct reset_file *reset_files;\n\n/* Triggers need to be cleared in a special way */\nstatic struct reset_file *reset_triggers;\n\nstruct buffer_instance top_instance;\nstruct buffer_instance *buffer_instances;\nstruct buffer_instance *first_instance;\n\nstatic struct tracecmd_recorder *recorder;\n\nstatic int ignore_event_not_found = 0;\n\nstatic inline int is_top_instance(struct buffer_instance *instance)\n{\n\treturn instance == &top_instance;\n}\n\nstatic inline int no_top_instance(void)\n{\n\treturn first_instance != &top_instance;\n}\n\nstatic void init_instance(struct buffer_instance *instance)\n{\n\tinstance->event_next = &instance->events;\n}\n\nenum {\n\tRESET_DEFAULT_PRIO\t= 0,\n\tRESET_HIGH_PRIO\t\t= 100000,\n};\n\nenum trace_cmd {\n\tCMD_extract,\n\tCMD_start,\n\tCMD_stream,\n\tCMD_profile,\n\tCMD_record,\n\tCMD_record_agent,\n\tCMD_set,\n};\n\nstruct common_record_context {\n\tenum trace_cmd curr_cmd;\n\tstruct buffer_instance *instance;\n\tconst char *output;\n\tconst char *temp;\n\tchar *date2ts;\n\tchar *user;\n\tconst char *clock;\n\tconst char *compression;\n\tstruct tsc_nsec tsc2nsec;\n\tint data_flags;\n\tint tsync_loop_interval;\n\n\tint record_all;\n\tint total_disable;\n\tint disable;\n\tint events;\n\tint global;\n\tint filtered;\n\tint date;\n\tint manual;\n\tint topt;\n\tint run_command;\n\tint saved_cmdlines_size;\n\tint file_version;\n};\n\nstatic void add_reset_file(const char *file, const char *val, int prio)\n{\n\tstruct reset_file *reset;\n\tstruct reset_file **last = &reset_files;\n\n\t/* Only reset if we are not keeping the state */\n\tif (keep)\n\t\treturn;\n\n\treset = malloc(sizeof(*reset));\n\tif (!reset)\n\t\tdie(\"Failed to allocate reset\");\n\treset->path = strdup(file);\n\treset->reset = strdup(val);\n\treset->prio = prio;\n\tif (!reset->path || !reset->reset)\n\t\tdie(\"Failed to allocate reset path or val\");\n\n\twhile (*last && (*last)->prio > prio)\n\t\tlast = &(*last)->next;\n\n\treset->next = *last;\n\t*last = reset;\n}\n\nstatic void add_reset_trigger(const char *file)\n{\n\tstruct reset_file *reset;\n\n\t/* Only reset if we are not keeping the state */\n\tif (keep)\n\t\treturn;\n\n\treset = malloc(sizeof(*reset));\n\tif (!reset)\n\t\tdie(\"Failed to allocate reset\");\n\treset->path = strdup(file);\n\n\treset->next = reset_triggers;\n\treset_triggers = reset;\n}\n\n/* To save the contents of the file */\nstatic void reset_save_file(const char *file, int prio)\n{\n\tchar *content;\n\n\tcontent = get_file_content(file);\n\tif (content) {\n\t\tadd_reset_file(file, content, prio);\n\t\tfree(content);\n\t}\n}\n\n/*\n * @file: the file to check\n * @nop: If the content of the file is this, use the reset value\n * @reset: What to write if the file == @nop\n */\nstatic void reset_save_file_cond(const char *file, int prio,\n\t\t\t\t const char *nop, const char *reset)\n{\n\tchar *content;\n\tchar *cond;\n\n\tif (keep)\n\t\treturn;\n\n\tcontent = get_file_content(file);\n\n\tcond = strstrip(content);\n\n\tif (strcmp(cond, nop) == 0)\n\t\tadd_reset_file(file, reset, prio);\n\telse\n\t\tadd_reset_file(file, content, prio);\n\n\tfree(content);\n}\n\n/**\n * add_instance - add a buffer instance to the internal list\n * @instance: The buffer instance to add\n */\nvoid add_instance(struct buffer_instance *instance, int cpu_count)\n{\n\tinit_instance(instance);\n\tinstance->next = buffer_instances;\n\tif (first_instance == buffer_instances)\n\t\tfirst_instance = instance;\n\tbuffer_instances = instance;\n\tinstance->cpu_count = cpu_count;\n\tbuffers++;\n}\n\nstatic void instance_reset_file_save(struct buffer_instance *instance, char *file, int prio)\n{\n\tchar *path;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, file);\n\tif (path)\n\t\treset_save_file(path, prio);\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void test_set_event_pid(struct buffer_instance *instance)\n{\n\tstatic int have_set_event_pid;\n\tstatic int have_event_fork;\n\tstatic int have_func_fork;\n\n\tif (!have_set_event_pid &&\n\t    tracefs_file_exists(top_instance.tracefs, \"set_event_pid\"))\n\t\thave_set_event_pid = 1;\n\tif (!have_event_fork &&\n\t    tracefs_file_exists(top_instance.tracefs, \"options/event-fork\"))\n\t\thave_event_fork = 1;\n\tif (!have_func_fork &&\n\t    tracefs_file_exists(top_instance.tracefs, \"options/function-fork\"))\n\t\thave_func_fork = 1;\n\n\tif (!instance->have_set_event_pid && have_set_event_pid) {\n\t\tinstance->have_set_event_pid = 1;\n\t\tinstance_reset_file_save(instance, \"set_event_pid\",\n\t\t\t\t\t RESET_DEFAULT_PRIO);\n\t}\n\tif (!instance->have_event_fork && have_event_fork) {\n\t\tinstance->have_event_fork = 1;\n\t\tinstance_reset_file_save(instance, \"options/event-fork\",\n\t\t\t\t\t RESET_DEFAULT_PRIO);\n\t}\n\tif (!instance->have_func_fork && have_func_fork) {\n\t\tinstance->have_func_fork = 1;\n\t\tinstance_reset_file_save(instance, \"options/function-fork\",\n\t\t\t\t\t RESET_DEFAULT_PRIO);\n\t}\n}\n\n/**\n * allocate_instance - allocate a new buffer instance,\n *\t\t\tit must exist in the ftrace system\n * @name: The name of the instance (instance will point to this)\n *\n * Returns a newly allocated instance. In case of an error or if the\n * instance does not exist in the ftrace system, NULL is returned.\n */\nstruct buffer_instance *allocate_instance(const char *name)\n{\n\tstruct buffer_instance *instance;\n\n\tinstance = calloc(1, sizeof(*instance));\n\tif (!instance)\n\t\treturn NULL;\n\tif (name)\n\t\tinstance->name = strdup(name);\n\tif (tracefs_instance_exists(name)) {\n\t\tinstance->tracefs = tracefs_instance_create(name);\n\t\tif (!instance->tracefs)\n\t\t\tgoto error;\n\t}\n\n\treturn instance;\n\nerror:\n\tfree(instance->name);\n\ttracefs_instance_free(instance->tracefs);\n\tfree(instance);\n\treturn NULL;\n}\n\nstatic int __add_all_instances(const char *tracing_dir)\n{\n\tstruct dirent *dent;\n\tchar *instances_dir;\n\tstruct stat st;\n\tDIR *dir;\n\tint ret;\n\n\tif (!tracing_dir)\n\t\treturn -1;\n\n\tinstances_dir = append_file(tracing_dir, \"instances\");\n\tif (!instances_dir)\n\t\treturn -1;\n\n\tret = stat(instances_dir, &st);\n\tif (ret < 0 || !S_ISDIR(st.st_mode)) {\n\t\tret = -1;\n\t\tgoto out_free;\n\t}\n\n\tdir = opendir(instances_dir);\n\tif (!dir) {\n\t\tret = -1;\n\t\tgoto out_free;\n\t}\n\n\twhile ((dent = readdir(dir))) {\n\t\tconst char *name = dent->d_name;\n\t\tchar *instance_path;\n\t\tstruct buffer_instance *instance;\n\n\t\tif (strcmp(name, \".\") == 0 ||\n\t\t    strcmp(name, \"..\") == 0)\n\t\t\tcontinue;\n\n\t\tinstance_path = append_file(instances_dir, name);\n\t\tret = stat(instance_path, &st);\n\t\tif (ret < 0 || !S_ISDIR(st.st_mode)) {\n\t\t\tfree(instance_path);\n\t\t\tcontinue;\n\t\t}\n\t\tfree(instance_path);\n\n\t\tinstance = allocate_instance(name);\n\t\tif (!instance)\n\t\t\tdie(\"Failed to create instance\");\n\t\tadd_instance(instance, local_cpu_count);\n\t}\n\n\tclosedir(dir);\n\tret = 0;\n\n out_free:\n\tfree(instances_dir);\n\treturn ret;\n}\n\n/**\n * add_all_instances - Add all pre-existing instances to the internal list\n * @tracing_dir: The top-level tracing directory\n *\n * Returns whether the operation succeeded\n */\nvoid add_all_instances(void)\n{\n\tconst char *tracing_dir = tracefs_tracing_dir();\n\tif (!tracing_dir)\n\t\tdie(\"can't get the tracing directory\");\n\n\t__add_all_instances(tracing_dir);\n}\n\n/**\n * tracecmd_stat_cpu - show the buffer stats of a particular CPU\n * @s: the trace_seq to record the data in.\n * @cpu: the CPU to stat\n *\n */\nvoid tracecmd_stat_cpu_instance(struct buffer_instance *instance,\n\t\t\t\tstruct trace_seq *s, int cpu)\n{\n\tchar buf[BUFSIZ];\n\tchar *path;\n\tchar *file;\n\tint fd;\n\tint r;\n\n\tfile = malloc(40);\n\tif (!file)\n\t\treturn;\n\tsnprintf(file, 40, \"per_cpu/cpu%d/stats\", cpu);\n\n\tpath = tracefs_instance_get_file(instance->tracefs, file);\n\tfree(file);\n\tfd = open(path, O_RDONLY);\n\ttracefs_put_tracing_file(path);\n\tif (fd < 0)\n\t\treturn;\n\n\twhile ((r = read(fd, buf, BUFSIZ)) > 0)\n\t\ttrace_seq_printf(s, \"%.*s\", r, buf);\n\n\tclose(fd);\n}\n\n/**\n * tracecmd_stat_cpu - show the buffer stats of a particular CPU\n * @s: the trace_seq to record the data in.\n * @cpu: the CPU to stat\n *\n */\nvoid tracecmd_stat_cpu(struct trace_seq *s, int cpu)\n{\n\ttracecmd_stat_cpu_instance(&top_instance, s, cpu);\n}\n\nstatic void add_event(struct buffer_instance *instance, struct event_list *event)\n{\n\t*instance->event_next = event;\n\tinstance->event_next = &event->next;\n\tevent->next = NULL;\n}\n\nstatic void reset_event_list(struct buffer_instance *instance)\n{\n\tinstance->events = NULL;\n\tinit_instance(instance);\n}\n\nstatic char *get_temp_file(struct buffer_instance *instance, int cpu)\n{\n\tconst char *output_file = instance->output_file;\n\tconst char *name;\n\tchar *file = NULL;\n\tint size;\n\n\tif (instance->temp_dir) {\n\t\tif (!instance->temp_file) {\n\t\t\tconst char *f = output_file + strlen(output_file) - 1;;\n\t\t\tint ret;\n\n\t\t\tfor (; f > output_file && *f != '/'; f--)\n\t\t\t\t;\n\t\t\tif (*f == '/')\n\t\t\t\tf++;\n\t\t\tret = asprintf(&instance->temp_file, \"%s/%s\",\n\t\t\t\t       instance->temp_dir, f);\n\t\t\tif (ret < 0)\n\t\t\t\tdie(\"Failed to create temp file\");\n\t\t}\n\t\toutput_file = instance->temp_file;\n\t}\n\n\tname = tracefs_instance_get_name(instance->tracefs);\n\tif (name) {\n\t\tsize = snprintf(file, 0, \"%s.%s.cpu%d\", output_file, name, cpu);\n\t\tfile = malloc(size + 1);\n\t\tif (!file)\n\t\t\tdie(\"Failed to allocate temp file for %s\", name);\n\t\tsprintf(file, \"%s.%s.cpu%d\", output_file, name, cpu);\n\t} else {\n\t\tsize = snprintf(file, 0, \"%s.cpu%d\", output_file, cpu);\n\t\tfile = malloc(size + 1);\n\t\tif (!file)\n\t\t\tdie(\"Failed to allocate temp file\");\n\t\tsprintf(file, \"%s.cpu%d\", output_file, cpu);\n\t}\n\n\treturn file;\n}\n\nchar *trace_get_guest_file(const char *file, const char *guest)\n{\n\tconst char *p;\n\tchar *out = NULL;\n\tint ret, base_len;\n\n\tp = strrchr(file, '.');\n\tif (p && p != file)\n\t\tbase_len = p - file;\n\telse\n\t\tbase_len = strlen(file);\n\n\tret = asprintf(&out, \"%.*s-%s%s\", base_len, file,\n\t\t       guest, file + base_len);\n\tif (ret < 0)\n\t\treturn NULL;\n\treturn out;\n}\n\nstatic void put_temp_file(char *file)\n{\n\tfree(file);\n}\n\nstatic void delete_temp_file(struct buffer_instance *instance, int cpu)\n{\n\tconst char *output_file;\n\tconst char *name;\n\tchar file[PATH_MAX];\n\n\tif (instance->temp_file)\n\t\toutput_file = instance->temp_file;\n\telse\n\t\toutput_file = instance->output_file;\n\n\tname = tracefs_instance_get_name(instance->tracefs);\n\tif (name)\n\t\tsnprintf(file, PATH_MAX, \"%s.%s.cpu%d\", output_file, name, cpu);\n\telse\n\t\tsnprintf(file, PATH_MAX, \"%s.cpu%d\", output_file, cpu);\n\tunlink(file);\n}\n\nstatic int kill_thread_instance(int start, struct buffer_instance *instance)\n{\n\tint n = start;\n\tint i;\n\n\tfor (i = 0; i < instance->cpu_count; i++) {\n\t\tif (pids[n].pid > 0) {\n\t\t\tkill(pids[n].pid, SIGKILL);\n\t\t\tdelete_temp_file(instance, i);\n\t\t\tpids[n].pid = 0;\n\t\t\tif (pids[n].brass[0] >= 0)\n\t\t\t\tclose(pids[n].brass[0]);\n\t\t}\n\t\tn++;\n\t}\n\n\treturn n;\n}\n\nstatic void kill_threads(void)\n{\n\tstruct buffer_instance *instance;\n\tint i = 0;\n\n\tif (!recorder_threads || !pids)\n\t\treturn;\n\n\tfor_all_instances(instance)\n\t\ti = kill_thread_instance(i, instance);\n}\n\nvoid die(const char *fmt, ...)\n{\n\tva_list ap;\n\tint ret = errno;\n\n\tif (errno)\n\t\tperror(\"trace-cmd\");\n\telse\n\t\tret = -1;\n\n\tif (created_pidfile)\n\t\tremove_pid_file(RECORD_PIDFILE);\n\n\tkill_threads();\n\tva_start(ap, fmt);\n\tfprintf(stderr, \"  \");\n\tvfprintf(stderr, fmt, ap);\n\tva_end(ap);\n\n\tfprintf(stderr, \"\\n\");\n\texit(ret);\n}\n\nstatic int delete_thread_instance(int start, struct buffer_instance *instance)\n{\n\tint n = start;\n\tint i;\n\n\tfor (i = 0; i < instance->cpu_count; i++) {\n\t\tif (pids) {\n\t\t\tif (pids[n].pid) {\n\t\t\t\tdelete_temp_file(instance, i);\n\t\t\t\tif (pids[n].pid < 0)\n\t\t\t\t\tpids[n].pid = 0;\n\t\t\t}\n\t\t\tn++;\n\t\t} else\n\t\t\t/* Extract does not allocate pids */\n\t\t\tdelete_temp_file(instance, i);\n\t}\n\treturn n;\n}\n\nstatic void delete_thread_data(void)\n{\n\tstruct buffer_instance *instance;\n\tint i = 0;\n\n\tfor_all_instances(instance)\n\t\ti = delete_thread_instance(i, instance);\n\t/*\n\t * Top instance temp files are still created even if it\n\t * isn't used.\n\t */\n\tif (no_top_instance()) {\n\t\tfor (i = 0; i < local_cpu_count; i++)\n\t\t\tdelete_temp_file(&top_instance, i);\n\t}\n}\n\nstatic void\nadd_tsc2nsec(struct tracecmd_output *handle, struct tsc_nsec *tsc2nsec)\n{\n\t/* multiplier, shift, offset */\n\tstruct iovec vector[3];\n\n\tvector[0].iov_len = 4;\n\tvector[0].iov_base = &tsc2nsec->mult;\n\tvector[1].iov_len = 4;\n\tvector[1].iov_base = &tsc2nsec->shift;\n\tvector[2].iov_len = 8;\n\tvector[2].iov_base = &tsc2nsec->offset;\n\n\ttracecmd_add_option_v(handle, TRACECMD_OPTION_TSC2NSEC, vector, 3);\n}\n\nstatic void guest_tsync_complete(struct buffer_instance *instance)\n{\n\ttracecmd_tsync_with_host_stop(instance->tsync);\n\ttracecmd_tsync_free(instance->tsync);\n}\n\nstatic void host_tsync_complete(struct common_record_context *ctx,\n\t\t\t\tstruct buffer_instance *instance)\n{\n\tstruct tracecmd_output *handle = NULL;\n\tint fd = -1;\n\tint ret;\n\n\tret = tracecmd_tsync_with_guest_stop(instance->tsync);\n\tif (!ret) {\n\t\tfd = open(instance->output_file, O_RDWR);\n\t\tif (fd < 0)\n\t\t\tdie(\"error opening %s\", instance->output_file);\n\t\thandle = tracecmd_get_output_handle_fd(fd);\n\t\tif (!handle)\n\t\t\tdie(\"cannot create output handle\");\n\n\t\tif (ctx->tsc2nsec.mult)\n\t\t\tadd_tsc2nsec(handle, &ctx->tsc2nsec);\n\n\t\ttracecmd_write_guest_time_shift(handle, instance->tsync);\n\t\ttracecmd_append_options(handle);\n\t\ttracecmd_output_close(handle);\n\t}\n\n\ttracecmd_tsync_free(instance->tsync);\n\tinstance->tsync = NULL;\n}\n\nstatic void tell_guests_to_stop(struct common_record_context *ctx)\n{\n\tstruct buffer_instance *instance;\n\n\t/* Send close message to guests */\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance)) {\n\t\t\ttracecmd_msg_send_close_msg(instance->msg_handle);\n\t\t\tif (is_proxy(instance) && instance->proxy_fd >= 0) {\n\t\t\t\t/* The proxy will send more data now */\n\t\t\t\tif (tracecmd_msg_read_data(instance->msg_handle, instance->proxy_fd))\n\t\t\t\t\twarning(\"Failed receiving finishing metadata\");\n\t\t\t\tclose(instance->proxy_fd);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance)) {\n\t\t\tif (is_proxy(instance))\n\t\t\t\tguest_tsync_complete(instance);\n\t\t\telse\n\t\t\t\thost_tsync_complete(ctx, instance);\n\t\t}\n\t}\n\n\t/* Wait for guests to acknowledge */\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance)) {\n\t\t\tif (!is_proxy(instance)) {\n\t\t\t\ttracecmd_msg_wait_close_resp(instance->msg_handle);\n\t\t\t\ttracecmd_msg_handle_close(instance->msg_handle);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void stop_threads(enum trace_type type)\n{\n\tint ret;\n\tint i;\n\n\tif (!recorder_threads)\n\t\treturn;\n\n\t/* Tell all threads to finish up */\n\tfor (i = 0; i < recorder_threads; i++) {\n\t\tif (pids[i].pid > 0) {\n\t\t\tkill(pids[i].pid, SIGUSR1);\n\t\t}\n\t}\n\n\t/* Flush out the pipes */\n\tif (type & TRACE_TYPE_STREAM) {\n\t\tdo {\n\t\t\tret = trace_stream_read(pids, recorder_threads, 0);\n\t\t} while (ret > 0);\n\t}\n}\n\nstatic void wait_threads()\n{\n\tint i;\n\n\tfor (i = 0; i < recorder_threads; i++) {\n\t\tif (pids[i].pid > 0) {\n\t\t\twaitpid(pids[i].pid, NULL, 0);\n\t\t\tpids[i].pid = -1;\n\t\t}\n\t}\n}\n\nstatic int create_recorder(struct buffer_instance *instance, int cpu,\n\t\t\t   enum trace_type type, int *brass);\n\nstatic void flush_threads(void)\n{\n\tstruct buffer_instance *instance;\n\tlong ret;\n\tint i;\n\n\tfor_all_instances(instance) {\n\t\tfor (i = 0; i < instance->cpu_count; i++) {\n\t\t\t/* Extract doesn't support sub buffers yet */\n\t\t\tret = create_recorder(instance, i, TRACE_TYPE_EXTRACT, NULL);\n\t\t\tif (ret < 0)\n\t\t\t\tdie(\"error reading ring buffer\");\n\t\t}\n\t}\n}\n\nstatic int set_ftrace_enable(const char *path, int set)\n{\n\tstruct stat st;\n\tint fd;\n\tchar *val = set ? \"1\" : \"0\";\n\tint ret;\n\n\t/* if ftace_enable does not exist, simply ignore it */\n\tfd = stat(path, &st);\n\tif (fd < 0)\n\t\treturn -ENODEV;\n\n\treset_save_file(path, RESET_DEFAULT_PRIO);\n\n\tret = -1;\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0)\n\t\tgoto out;\n\n\t/* Now set or clear the function option */\n\tret = write(fd, val, 1);\n\tclose(fd);\n\n out:\n\treturn ret < 0 ? ret : 0;\n}\n\nstatic int set_ftrace_proc(int set)\n{\n\tconst char *path = \"/proc/sys/kernel/ftrace_enabled\";\n\tint ret;\n\n\tret = set_ftrace_enable(path, set);\n\tif (ret == -1)\n\t\tdie (\"Can't %s ftrace\", set ? \"enable\" : \"disable\");\n\treturn ret;\n}\n\nstatic int set_ftrace(struct buffer_instance *instance, int set, int use_proc)\n{\n\tchar *path;\n\tint ret;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"options/function-trace\");\n\tif (!path)\n\t\treturn -1;\n\tret = set_ftrace_enable(path, set);\n\ttracefs_put_tracing_file(path);\n\n\t/* Always enable ftrace_enable proc file when set is true */\n\tif (ret < 0 || set || use_proc)\n\t\tret = set_ftrace_proc(set);\n\n\treturn ret;\n}\n\nstatic int write_file(const char *file, const char *str)\n{\n\tint ret;\n\tint fd;\n\n\tfd = open(file, O_WRONLY | O_TRUNC);\n\tif (fd < 0)\n\t\tdie(\"opening to '%s'\", file);\n\tret = write(fd, str, strlen(str));\n\tclose(fd);\n\treturn ret;\n}\n\nstatic void __clear_trace(struct buffer_instance *instance)\n{\n\tFILE *fp;\n\tchar *path;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\t/* reset the trace */\n\tpath = tracefs_instance_get_file(instance->tracefs, \"trace\");\n\tfp = fopen(path, \"w\");\n\tif (!fp)\n\t\tdie(\"writing to '%s'\", path);\n\ttracefs_put_tracing_file(path);\n\tfwrite(\"0\", 1, 1, fp);\n\tfclose(fp);\n}\n\nstatic void clear_trace_instances(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\t__clear_trace(instance);\n}\n\nstatic void reset_max_latency(struct buffer_instance *instance)\n{\n\ttracefs_instance_file_write(instance->tracefs,\n\t\t\t\t    \"tracing_max_latency\", \"0\");\n}\n\nstatic int add_filter_pid(struct buffer_instance *instance, int pid, int exclude)\n{\n\tstruct filter_pids *p;\n\tchar buf[100];\n\n\tfor (p = instance->filter_pids; p; p = p->next) {\n\t\tif (p->pid == pid) {\n\t\t\tp->exclude = exclude;\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tp = malloc(sizeof(*p));\n\tif (!p)\n\t\tdie(\"Failed to allocate pid filter\");\n\tp->next = instance->filter_pids;\n\tp->exclude = exclude;\n\tp->pid = pid;\n\tinstance->filter_pids = p;\n\tinstance->nr_filter_pids++;\n\n\tinstance->len_filter_pids += sprintf(buf, \"%d\", pid);\n\n\treturn 1;\n}\n\nstatic void add_filter_pid_all(int pid, int exclude)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tadd_filter_pid(instance, pid, exclude);\n}\n\nstatic void reset_save_ftrace_pid(struct buffer_instance *instance)\n{\n\tstatic char *path;\n\n\tif (!tracefs_file_exists(instance->tracefs, \"set_ftrace_pid\"))\n\t\treturn;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"set_ftrace_pid\");\n\tif (!path)\n\t\treturn;\n\n\treset_save_file_cond(path, RESET_DEFAULT_PRIO, \"no pid\", \"\");\n\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void update_ftrace_pid(struct buffer_instance *instance,\n\t\t\t      const char *pid, int reset)\n{\n\tint fd = -1;\n\tchar *path;\n\tint ret;\n\n\tif (!tracefs_file_exists(instance->tracefs, \"set_ftrace_pid\"))\n\t\treturn;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"set_ftrace_pid\");\n\tif (!path)\n\t\treturn;\n\n\tfd = open(path, O_WRONLY | O_CLOEXEC | (reset ? O_TRUNC : 0));\n\ttracefs_put_tracing_file(path);\n\tif (fd < 0)\n\t\treturn;\n\n\tret = write(fd, pid, strlen(pid));\n\n\t/*\n\t * Older kernels required \"-1\" to disable pid\n\t */\n\tif (ret < 0 && !strlen(pid))\n\t\tret = write(fd, \"-1\", 2);\n\n\tif (ret < 0)\n\t\tdie(\"error writing to %s\", path);\n\t/* add whitespace in case another pid is written */\n\twrite(fd, \" \", 1);\n\tclose(fd);\n}\n\nstatic void update_ftrace_pids(int reset)\n{\n\tstruct buffer_instance *instance;\n\tstruct filter_pids *pid;\n\tstatic int first = 1;\n\tchar buf[100];\n\tint rst;\n\n\tfor_all_instances(instance) {\n\t\tif (first)\n\t\t\treset_save_ftrace_pid(instance);\n\t\trst = reset;\n\t\tfor (pid = instance->filter_pids; pid; pid = pid->next) {\n\t\t\tif (pid->exclude)\n\t\t\t\tcontinue;\n\t\t\tsnprintf(buf, 100, \"%d \", pid->pid);\n\t\t\tupdate_ftrace_pid(instance, buf, rst);\n\t\t\t/* Only reset the first entry */\n\t\t\trst = 0;\n\t\t}\n\t}\n\n\tif (first)\n\t\tfirst = 0;\n}\n\nstatic void update_event_filters(struct buffer_instance *instance);\nstatic void update_pid_event_filters(struct buffer_instance *instance);\n\nstatic void append_filter_pid_range(char **filter, int *curr_len,\n\t\t\t\t    const char *field,\n\t\t\t\t    int start_pid, int end_pid, bool exclude)\n{\n\tconst char *op = \"\", *op1, *op2, *op3;\n\tint len;\n\n\tif (*filter && **filter)\n\t\top = exclude ? \"&&\" : \"||\";\n\n\t/* Handle thus case explicitly so that we get `pid==3` instead of\n\t * `pid>=3&&pid<=3` for singleton ranges\n\t */\n\tif (start_pid == end_pid) {\n#define FMT\t\"%s(%s%s%d)\"\n\t\tlen = snprintf(NULL, 0, FMT, op,\n\t\t\t       field, exclude ? \"!=\" : \"==\", start_pid);\n\t\t*filter = realloc(*filter, *curr_len + len + 1);\n\t\tif (!*filter)\n\t\t\tdie(\"realloc\");\n\n\t\tlen = snprintf(*filter + *curr_len, len + 1, FMT, op,\n\t\t\t       field, exclude ? \"!=\" : \"==\", start_pid);\n\t\t*curr_len += len;\n\n\t\treturn;\n#undef FMT\n\t}\n\n\tif (exclude) {\n\t\top1 = \"<\";\n\t\top2 = \"||\";\n\t\top3 = \">\";\n\t} else {\n\t\top1 = \">=\";\n\t\top2 = \"&&\";\n\t\top3 = \"<=\";\n\t}\n\n#define FMT\t\"%s(%s%s%d%s%s%s%d)\"\n\tlen = snprintf(NULL, 0, FMT, op,\n\t\t       field, op1, start_pid, op2,\n\t\t       field, op3, end_pid);\n\t*filter = realloc(*filter, *curr_len + len + 1);\n\tif (!*filter)\n\t\tdie(\"realloc\");\n\n\tlen = snprintf(*filter + *curr_len, len + 1, FMT, op,\n\t\t       field, op1, start_pid, op2,\n\t\t       field, op3, end_pid);\n\t*curr_len += len;\n}\n\n/**\n * make_pid_filter - create a filter string to all pids against @field\n * @curr_filter: Append to a previous filter (may realloc). Can be NULL\n * @field: The field to compare the pids against\n *\n * Creates a new string or appends to an existing one if @curr_filter\n * is not NULL. The new string will contain a filter with all pids\n * in pid_filter list with the format (@field == pid) || ..\n * If @curr_filter is not NULL, it will add this string as:\n *  (@curr_filter) && ((@field == pid) || ...)\n */\nstatic char *make_pid_filter(struct buffer_instance *instance,\n\t\t\t     char *curr_filter, const char *field)\n{\n\tint start_pid = -1, last_pid = -1;\n\tint last_exclude = -1;\n\tstruct filter_pids *p;\n\tchar *filter = NULL;\n\tint curr_len = 0;\n\n\t/* Use the new method if possible */\n\tif (instance->have_set_event_pid)\n\t\treturn NULL;\n\n\tif (!instance->filter_pids)\n\t\treturn curr_filter;\n\n\tfor (p = instance->filter_pids; p; p = p->next) {\n\t\t/*\n\t\t * PIDs are inserted in `filter_pids` from the front and that's\n\t\t * why we expect them in descending order here.\n\t\t */\n\t\tif (p->pid == last_pid - 1 && p->exclude == last_exclude) {\n\t\t\tlast_pid = p->pid;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (start_pid != -1)\n\t\t\tappend_filter_pid_range(&filter, &curr_len, field,\n\t\t\t\t\t\tlast_pid, start_pid,\n\t\t\t\t\t\tlast_exclude);\n\n\t\tstart_pid = last_pid = p->pid;\n\t\tlast_exclude = p->exclude;\n\n\t}\n\tappend_filter_pid_range(&filter, &curr_len, field,\n\t\t\t\tlast_pid, start_pid, last_exclude);\n\n\tif (curr_filter) {\n\t\tchar *save = filter;\n\t\tasprintf(&filter, \"(%s)&&(%s)\", curr_filter, filter);\n\t\tfree(save);\n\t}\n\n\treturn filter;\n}\n\n#define _STRINGIFY(x) #x\n#define STRINGIFY(x) _STRINGIFY(x)\n\nstatic int get_pid_addr_maps(struct buffer_instance *instance, int pid)\n{\n\tstruct pid_addr_maps *maps = instance->pid_maps;\n\tstruct tracecmd_proc_addr_map *map;\n\tunsigned long long begin, end;\n\tstruct pid_addr_maps *m;\n\tchar mapname[PATH_MAX+1];\n\tchar fname[PATH_MAX+1];\n\tchar buf[PATH_MAX+100];\n\tFILE *f;\n\tint ret;\n\tint res;\n\tint i;\n\n\tsprintf(fname, \"/proc/%d/exe\", pid);\n\tret = readlink(fname, mapname, PATH_MAX);\n\tif (ret >= PATH_MAX || ret < 0)\n\t\treturn -ENOENT;\n\tmapname[ret] = 0;\n\n\tsprintf(fname, \"/proc/%d/maps\", pid);\n\tf = fopen(fname, \"r\");\n\tif (!f)\n\t\treturn -ENOENT;\n\n\twhile (maps) {\n\t\tif (pid == maps->pid)\n\t\t\tbreak;\n\t\tmaps = maps->next;\n\t}\n\n\tret = -ENOMEM;\n\tif (!maps) {\n\t\tmaps = calloc(1, sizeof(*maps));\n\t\tif (!maps)\n\t\t\tgoto out_fail;\n\t\tmaps->pid = pid;\n\t\tmaps->next = instance->pid_maps;\n\t\tinstance->pid_maps = maps;\n\t} else {\n\t\tfor (i = 0; i < maps->nr_lib_maps; i++)\n\t\t\tfree(maps->lib_maps[i].lib_name);\n\t\tfree(maps->lib_maps);\n\t\tmaps->lib_maps = NULL;\n\t\tmaps->nr_lib_maps = 0;\n\t\tfree(maps->proc_name);\n\t}\n\n\tmaps->proc_name = strdup(mapname);\n\tif (!maps->proc_name)\n\t\tgoto out;\n\n\twhile (fgets(buf, sizeof(buf), f)) {\n\t\tmapname[0] = '\\0';\n\t\tres = sscanf(buf, \"%llx-%llx %*s %*x %*s %*d %\"STRINGIFY(PATH_MAX)\"s\",\n\t\t\t     &begin, &end, mapname);\n\t\tif (res == 3 && mapname[0] != '\\0') {\n\t\t\tmap = realloc(maps->lib_maps,\n\t\t\t\t      (maps->nr_lib_maps + 1) * sizeof(*map));\n\t\t\tif (!map)\n\t\t\t\tgoto out_fail;\n\t\t\tmaps->lib_maps = map;\n\t\t\tmap[maps->nr_lib_maps].end = end;\n\t\t\tmap[maps->nr_lib_maps].start = begin;\n\t\t\tmap[maps->nr_lib_maps].lib_name = strdup(mapname);\n\t\t\tif (!map[maps->nr_lib_maps].lib_name)\n\t\t\t\tgoto out_fail;\n\t\t\tmaps->nr_lib_maps++;\n\t\t}\n\t}\nout:\n\tfclose(f);\n\treturn 0;\n\nout_fail:\n\tfclose(f);\n\tif (maps) {\n\t\tfor (i = 0; i < maps->nr_lib_maps; i++)\n\t\t\tfree(maps->lib_maps[i].lib_name);\n\t\tif (instance->pid_maps != maps) {\n\t\t\tm = instance->pid_maps;\n\t\t\twhile (m) {\n\t\t\t\tif (m->next == maps) {\n\t\t\t\t\tm->next = maps->next;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tm = m->next;\n\t\t\t}\n\t\t} else\n\t\t\tinstance->pid_maps = maps->next;\n\t\tfree(maps->lib_maps);\n\t\tmaps->lib_maps = NULL;\n\t\tmaps->nr_lib_maps = 0;\n\t\tfree(maps->proc_name);\n\t\tmaps->proc_name = NULL;\n\t\tfree(maps);\n\t}\n\treturn ret;\n}\n\nstatic void get_filter_pid_maps(void)\n{\n\tstruct buffer_instance *instance;\n\tstruct filter_pids *p;\n\n\tfor_all_instances(instance) {\n\t\tif (!instance->get_procmap)\n\t\t\tcontinue;\n\t\tfor (p = instance->filter_pids; p; p = p->next) {\n\t\t\tif (p->exclude)\n\t\t\t\tcontinue;\n\t\t\tget_pid_addr_maps(instance, p->pid);\n\t\t}\n\t}\n}\n\nstatic void update_task_filter(void)\n{\n\tstruct buffer_instance *instance;\n\tint pid = getpid();\n\n\tif (no_filter)\n\t\treturn;\n\n\tget_filter_pid_maps();\n\n\tif (filter_task)\n\t\tadd_filter_pid_all(pid, 0);\n\n\tfor_all_instances(instance) {\n\t\tif (!instance->filter_pids)\n\t\t\tcontinue;\n\t\tif (instance->common_pid_filter)\n\t\t\tfree(instance->common_pid_filter);\n\t\tinstance->common_pid_filter = make_pid_filter(instance, NULL,\n\t\t\t\t\t\t\t      \"common_pid\");\n\t}\n\tupdate_ftrace_pids(1);\n\tfor_all_instances(instance)\n\t\tupdate_pid_event_filters(instance);\n}\n\nstatic pid_t trace_waitpid(enum trace_type type, pid_t pid, int *status, int options)\n{\n\tint ret;\n\n\tif (type & TRACE_TYPE_STREAM)\n\t\toptions |= WNOHANG;\n\n\tdo {\n\t\tret = waitpid(pid, status, options);\n\t\tif (ret != 0)\n\t\t\treturn ret;\n\n\t\tif (type & TRACE_TYPE_STREAM)\n\t\t\ttrace_stream_read(pids, recorder_threads, sleep_time);\n\t} while (1);\n}\n\n#ifndef __NR_pidfd_open\n#define __NR_pidfd_open 434\n#endif\n\nstatic int pidfd_open(pid_t pid, unsigned int flags) {\n\treturn syscall(__NR_pidfd_open, pid, flags);\n}\n\nstatic int trace_waitpidfd(id_t pidfd) {\n\tstruct pollfd pollfd;\n\n\tpollfd.fd = pidfd;\n\tpollfd.events = POLLIN;\n\n\twhile (!finished) {\n\t\tint ret = poll(&pollfd, 1, -1);\n\t\t/* If waitid was interrupted, keep waiting */\n\t\tif (ret < 0 && errno == EINTR)\n\t\t\tcontinue;\n\t\telse if (ret < 0)\n\t\t\treturn 1;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\nstatic int trace_wait_for_processes(struct buffer_instance *instance) {\n\tint ret = 0;\n\tint nr_fds = 0;\n\tint i;\n\tint *pidfds;\n\tstruct filter_pids *pid;\n\n\tpidfds = malloc(sizeof(int) * instance->nr_process_pids);\n\tif (!pidfds)\n\t\treturn 1;\n\n\tfor (pid = instance->process_pids;\n\t     pid && instance->nr_process_pids;\n\t     pid = pid->next) {\n\t\tif (pid->exclude) {\n\t\t\tinstance->nr_process_pids--;\n\t\t\tcontinue;\n\t\t}\n\t\tpidfds[nr_fds] = pidfd_open(pid->pid, 0);\n\n\t\t/* If the pid doesn't exist, the process has probably exited */\n\t\tif (pidfds[nr_fds] < 0 && errno == ESRCH) {\n\t\t\tinstance->nr_process_pids--;\n\t\t\tcontinue;\n\t\t} else if (pidfds[nr_fds] < 0) {\n\t\t\tret = 1;\n\t\t\tgoto out;\n\t\t}\n\n\t\tnr_fds++;\n\t\tinstance->nr_process_pids--;\n\t}\n\n\tfor (i = 0; i < nr_fds; i++) {\n\t\tif (trace_waitpidfd(pidfds[i])) {\n\t\t\tret = 1;\n\t\t\tgoto out;\n\t\t}\n\t}\n\nout:\n\tfor (i = 0; i < nr_fds; i++)\n\t\tclose(pidfds[i]);\n\tfree(pidfds);\n\treturn ret;\n}\n\nstatic void add_event_pid(struct buffer_instance *instance, const char *buf)\n{\n\ttracefs_instance_file_write(instance->tracefs, \"set_event_pid\", buf);\n}\n\n#ifndef NO_PTRACE\n/**\n * append_pid_filter - add a new pid to an existing filter\n * @curr_filter: the filter to append to. If NULL, then allocate one\n * @field: The fild to compare the pid to\n * @pid: The pid to add to.\n */\nstatic char *append_pid_filter(char *curr_filter, const char *field, int pid)\n{\n\tchar *filter;\n\tint len;\n\n\tlen = snprintf(NULL, 0, \"(%s==%d)||\", field, pid);\n\n\tif (!curr_filter) {\n\t\t/* No need for +1 as we don't use the \"||\" */\n\t\tfilter = malloc(len);\n\t\tif (!filter)\n\t\t\tdie(\"Failed to allocate pid filter\");\n\t\tsprintf(filter, \"(%s==%d)\", field, pid);\n\t} else {\n\t\tint indx = strlen(curr_filter);\n\n\t\tlen += indx;\n\t\tfilter = realloc(curr_filter, len + indx + 1);\n\t\tif (!filter)\n\t\t\tdie(\"realloc\");\n\t\tsprintf(filter + indx, \"||(%s==%d)\", field, pid);\n\t}\n\n\treturn filter;\n}\n\nstatic void append_sched_event(struct event_list *event, const char *field, int pid)\n{\n\tif (!event || !event->pid_filter)\n\t\treturn;\n\n\tevent->pid_filter = append_pid_filter(event->pid_filter, field, pid);\n}\n\nstatic void update_sched_events(struct buffer_instance *instance, int pid)\n{\n\t/*\n\t * Also make sure that the sched_switch to this pid\n\t * and wakeups of this pid are also traced.\n\t * Only need to do this if the events are active.\n\t */\n\tappend_sched_event(instance->sched_switch_event, \"next_pid\", pid);\n\tappend_sched_event(instance->sched_wakeup_event, \"pid\", pid);\n\tappend_sched_event(instance->sched_wakeup_new_event, \"pid\", pid);\n}\n\nstatic int open_instance_fd(struct buffer_instance *instance,\n\t\t\t    const char *file, int flags);\n\nstatic void add_new_filter_child_pid(int pid, int child)\n{\n\tstruct buffer_instance *instance;\n\tstruct filter_pids *fpid;\n\tchar buf[100];\n\n\tfor_all_instances(instance) {\n\t\tif (!instance->ptrace_child || !instance->filter_pids)\n\t\t\tcontinue;\n\t\tfor (fpid = instance->filter_pids; fpid; fpid = fpid->next) {\n\t\t\tif (fpid->pid == pid)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!fpid)\n\t\t\tcontinue;\n\n\t\tadd_filter_pid(instance, child, 0);\n\t\tsprintf(buf, \"%d\", child);\n\t\tupdate_ftrace_pid(instance, buf, 0);\n\n\t\tinstance->common_pid_filter = append_pid_filter(instance->common_pid_filter,\n\t\t\t\t\t\t\t\t\"common_pid\", pid);\n\t\tif (instance->have_set_event_pid) {\n\t\t\tadd_event_pid(instance, buf);\n\t\t} else {\n\t\t\tupdate_sched_events(instance, pid);\n\t\t\tupdate_event_filters(instance);\n\t\t}\n\t}\n\n}\n\nstatic void ptrace_attach(struct buffer_instance *instance, int pid)\n{\n\tint ret;\n\n\tret = ptrace(PTRACE_ATTACH, pid, NULL, 0);\n\tif (ret < 0) {\n\t\twarning(\"Unable to trace process %d children\", pid);\n\t\tdo_ptrace = 0;\n\t\treturn;\n\t}\n\tif (instance)\n\t\tadd_filter_pid(instance, pid, 0);\n\telse\n\t\tadd_filter_pid_all(pid, 0);\n}\n\nstatic void enable_ptrace(void)\n{\n\tif (!do_ptrace || !filter_task)\n\t\treturn;\n\n\tptrace(PTRACE_TRACEME, 0, NULL, 0);\n}\n\nstatic struct buffer_instance *get_intance_fpid(int pid)\n{\n\tstruct buffer_instance *instance;\n\tstruct filter_pids *fpid;\n\n\tfor_all_instances(instance) {\n\t\tfor (fpid = instance->filter_pids; fpid; fpid = fpid->next) {\n\t\t\tif (fpid->exclude)\n\t\t\t\tcontinue;\n\t\t\tif (fpid->pid == pid)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (fpid)\n\t\t\treturn instance;\n\t}\n\n\treturn NULL;\n}\n\nstatic void ptrace_wait(enum trace_type type)\n{\n\tstruct buffer_instance *instance;\n\tstruct filter_pids *fpid;\n\tunsigned long send_sig;\n\tunsigned long child;\n\tint nr_pids = 0;\n\tsiginfo_t sig;\n\tint main_pids;\n\tint cstatus;\n\tint status;\n\tint i = 0;\n\tint *pids;\n\tint event;\n\tint pid;\n\tint ret;\n\n\n\tfor_all_instances(instance)\n\t\tnr_pids += instance->nr_filter_pids;\n\n\tpids = calloc(nr_pids, sizeof(int));\n\tif (!pids) {\n\t\twarning(\"Unable to allocate array for %d PIDs\", nr_pids);\n\t\treturn;\n\t}\n\tfor_all_instances(instance) {\n\t\tif (!instance->ptrace_child && !instance->get_procmap)\n\t\t\tcontinue;\n\n\t\tfor (fpid = instance->filter_pids; fpid && i < nr_pids; fpid = fpid->next) {\n\t\t\tif (fpid->exclude)\n\t\t\t\tcontinue;\n\t\t\tpids[i++] = fpid->pid;\n\t\t}\n\t}\n\tmain_pids = i;\n\n\tdo {\n\t\tret = trace_waitpid(type, -1, &status, WSTOPPED | __WALL);\n\t\tif (ret < 0)\n\t\t\tcontinue;\n\n\t\tpid = ret;\n\n\t\tif (WIFSTOPPED(status)) {\n\t\t\tevent = (status >> 16) & 0xff;\n\t\t\tptrace(PTRACE_GETSIGINFO, pid, NULL, &sig);\n\t\t\tsend_sig = sig.si_signo;\n\t\t\t/* Don't send ptrace sigs to child */\n\t\t\tif (send_sig == SIGTRAP || send_sig == SIGSTOP)\n\t\t\t\tsend_sig = 0;\n\t\t\tswitch (event) {\n\t\t\tcase PTRACE_EVENT_FORK:\n\t\t\tcase PTRACE_EVENT_VFORK:\n\t\t\tcase PTRACE_EVENT_CLONE:\n\t\t\t\t/* forked a child */\n\t\t\t\tptrace(PTRACE_GETEVENTMSG, pid, NULL, &child);\n\t\t\t\tptrace(PTRACE_SETOPTIONS, child, NULL,\n\t\t\t\t       PTRACE_O_TRACEFORK |\n\t\t\t\t       PTRACE_O_TRACEVFORK |\n\t\t\t\t       PTRACE_O_TRACECLONE |\n\t\t\t\t       PTRACE_O_TRACEEXIT);\n\t\t\t\tadd_new_filter_child_pid(pid, child);\n\t\t\t\tptrace(PTRACE_CONT, child, NULL, 0);\n\t\t\t\tbreak;\n\n\t\t\tcase PTRACE_EVENT_EXIT:\n\t\t\t\tinstance = get_intance_fpid(pid);\n\t\t\t\tif (instance && instance->get_procmap)\n\t\t\t\t\tget_pid_addr_maps(instance, pid);\n\t\t\t\tptrace(PTRACE_GETEVENTMSG, pid, NULL, &cstatus);\n\t\t\t\tptrace(PTRACE_DETACH, pid, NULL, NULL);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tptrace(PTRACE_SETOPTIONS, pid, NULL,\n\t\t\t       PTRACE_O_TRACEFORK |\n\t\t\t       PTRACE_O_TRACEVFORK |\n\t\t\t       PTRACE_O_TRACECLONE |\n\t\t\t       PTRACE_O_TRACEEXIT);\n\t\t\tptrace(PTRACE_CONT, pid, NULL, send_sig);\n\t\t}\n\t\tif (WIFEXITED(status) ||\n\t\t   (WIFSTOPPED(status) && event == PTRACE_EVENT_EXIT)) {\n\t\t\tfor (i = 0; i < nr_pids; i++) {\n\t\t\t\tif (pid == pids[i]) {\n\t\t\t\t\tpids[i] = 0;\n\t\t\t\t\tmain_pids--;\n\t\t\t\t\tif (!main_pids)\n\t\t\t\t\t\tfinished = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} while (!finished && ret > 0);\n\n\tfree(pids);\n}\n#else\nstatic inline void ptrace_wait(enum trace_type type) { }\nstatic inline void enable_ptrace(void) { }\nstatic inline void ptrace_attach(struct buffer_instance *instance, int pid) { }\n\n#endif /* NO_PTRACE */\n\nstatic bool child_detached;\n\nstatic void daemonize_set_child_detached(int s)\n{\n\tchild_detached = true;\n}\n\nstatic void daemonize_start(void)\n{\n\tint devnull;\n\tint status;\n\tint pid;\n\tint ret;\n\n\tpid = fork();\n\tif (pid == -1)\n\t\tdie(\"daemonize: fork failed\");\n\n\tif (pid == 0) { /* child */\n\t\t/*\n\t\t * We keep stdout and stderr open to allow the user to\n\t\t * see output and errors after the daemonization (the user can\n\t\t * choose to supress it with >/dev/null if the user wants).\n\t\t *\n\t\t * No reason to keep stdin open (it might interfere with the\n\t\t * shell), we redirect it to /dev/null.\n\t\t */\n\t\tdevnull = open(\"/dev/null\", O_RDONLY);\n\t\tif (devnull == -1)\n\t\t\tdie(\"daemonize: open /dev/null failed\");\n\n\t\tif (devnull > 0) {\n\t\t\tif (dup2(devnull, 0) == -1)\n\t\t\t\tdie(\"daemonize: dup2\");\n\t\t\tclose(devnull);\n\t\t}\n\n\t\treturn;\n\n\t\t/*\n\t\t * The child returns to back to the caller, but the parent waits until\n\t\t * SIGRTMIN is received from the child (by calling daemonize_finish()),\n\t\t * or the child exits for some reason (usually an indication of\n\t\t * an error), which ever comes first.\n\t\t *\n\t\t * Then the parent exits (with the status code of the child,\n\t\t * if it finished early, or with 0 if SIGRTMIN was received),\n\t\t * which causes the child (and its entire process tree) to be\n\t\t * inherited by init.\n\t\t *\n\t\t * Note that until the child calls daemonize_finish(), it still has\n\t\t * the same session id as the parent, so it can die together with\n\t\t * the parent before daemonization finished (purposefully, since the\n\t\t * user might send a quick Ctrl^C to cancel the command, and we don't\n\t\t * want background processes staying alive in that case)\n\t\t */\n\t} else { /* parent */\n\t\tstruct sigaction sa = {\n\t\t\t/* disable SA_RESTART, to allow waitpid() to be interrupted by SIGRTMIN */\n\t\t\t.sa_flags = 0,\n\t\t\t.sa_handler = daemonize_set_child_detached\n\t\t};\n\n\t\tif (sigemptyset(&sa.sa_mask) == -1)\n\t\t\tdie(\"daemonize: sigemptyset failed\");\n\n\t\tif (sigaddset(&sa.sa_mask, SIGRTMIN) == -1)\n\t\t\tdie(\"daemonize: sigaddset failed\");\n\n\t\tif (sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL) == -1)\n\t\t\tdie(\"daemonize: sigprocmask failed\");\n\n\t\tif (sigaction(SIGRTMIN, &sa, NULL) == -1)\n\t\t\tdie(\"daemonize: sigaction failed\");\n\n\t\tdo {\n\t\t\tret = waitpid(pid, &status, 0);\n\t\t} while (!child_detached && ((ret < 0) && (errno == EINTR)));\n\n\t\tif (child_detached)\n\t\t\texit(0);\n\t\telse if (ret == pid)\n\t\t\texit(WIFEXITED(status));\n\t\telse\n\t\t\tdie(\"daemonize: waitpid failed\");\n\n\t\t__builtin_unreachable();\n\t}\n}\n\nstatic void daemonize_finish(void)\n{\n\t/*\n\t * setsid() will also set the sid to be the pgid to all currently\n\t * running threads in the process group (such as the tsync thread).\n\t */\n\tif (setsid() == -1)\n\t\tdie(\"daemonize: setsid\");\n\n\tif (kill(getppid(), SIGRTMIN) == -1)\n\t\tdie(\"daemonize: kill\");\n\n\tmake_pid_file(RECORD_PIDFILE);\n\tcreated_pidfile = true;\n}\n\nstatic void trace_or_sleep(enum trace_type type, bool pwait)\n{\n\tint i;\n\n\tif (pwait)\n\t\tptrace_wait(type);\n\telse if (type & TRACE_TYPE_STREAM) {\n\t\t/* Returns zero if it did not read anything (and did a sleep) */\n\t\tif (trace_stream_read(pids, recorder_threads, sleep_time) > 0)\n\t\t\treturn;\n\t\t/* Force a flush if nothing was read (including on errors) */\n\t\tfor (i = 0; i < recorder_threads; i++) {\n\t\t\tif (pids[i].pid > 0) {\n\t\t\t\tkill(pids[i].pid, SIGUSR2);\n\t\t\t}\n\t\t}\n\t} else\n\t\tsleep(10);\n}\n\nstatic int change_user(const char *user)\n{\n\tstruct passwd *pwd;\n\n\tif (!user)\n\t\treturn 0;\n\n\tpwd = getpwnam(user);\n\tif (!pwd)\n\t\treturn -1;\n\tif (initgroups(user, pwd->pw_gid) < 0)\n\t\treturn -1;\n\tif (setgid(pwd->pw_gid) < 0)\n\t\treturn -1;\n\tif (setuid(pwd->pw_uid) < 0)\n\t\treturn -1;\n\n\tif (setenv(\"HOME\", pwd->pw_dir, 1) < 0)\n\t\treturn -1;\n\tif (setenv(\"USER\", pwd->pw_name, 1) < 0)\n\t\treturn -1;\n\tif (setenv(\"LOGNAME\", pwd->pw_name, 1) < 0)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic void execute_program(int argc, char **argv)\n{\n\tchar buf[PATH_MAX + NAME_MAX + 1];\n\tchar *path;\n\tchar *entry;\n\tchar *saveptr;\n\n\t/*\n\t * if command specified by user is neither absolute nor\n\t * relative than we search for it in $PATH.\n\t */\n\tif (!strchr(argv[0], '/')) {\n\t\tpath = getenv(\"PATH\");\n\n\t\tif (!path)\n\t\t\tdie(\"can't search for '%s' if $PATH is NULL\", argv[0]);\n\n\t\t/* Do not modify the actual environment variable */\n\t\tpath = strdup(path);\n\t\tif (!path)\n\t\t\tdie(\"Failed to allocate PATH\");\n\n\t\tfor (entry = strtok_r(path, \":\", &saveptr);\n\t\t     entry; entry = strtok_r(NULL, \":\", &saveptr)) {\n\n\t\t\tsnprintf(buf, sizeof(buf), \"%s/%s\", entry, argv[0]);\n\n\t\t\t/* does it exist and can we execute it? */\n\t\t\tif (access(buf, X_OK) == 0)\n\t\t\t\tbreak;\n\n\t\t}\n\t\tfree(path);\n\t} else {\n\t\tstrncpy(buf, argv[0], sizeof(buf));\n\t}\n\n\ttracecmd_enable_tracing();\n\tif (execve(buf, argv, environ)) {\n\t\tfprintf(stderr, \"\\n********************\\n\");\n\t\tfprintf(stderr, \" Unable to exec %s\\n\", argv[0]);\n\t\tfprintf(stderr, \"********************\\n\");\n\t\tdie(\"Failed to exec %s\", argv[0]);\n\t}\n}\n\nstatic void run_cmd(enum trace_type type, const char *user, int argc, char **argv)\n{\n\tint status;\n\tint pid;\n\n\tif ((pid = fork()) < 0)\n\t\tdie(\"failed to fork\");\n\tif (!pid) {\n\t\t/* child */\n\t\tupdate_task_filter();\n\t\tif (!fork_process)\n\t\t\tenable_ptrace();\n\t\t/*\n\t\t * If we are using stderr for stdout, switch\n\t\t * it back to the saved stdout for the code we run.\n\t\t */\n\t\tif (save_stdout >= 0) {\n\t\t\tclose(1);\n\t\t\tdup2(save_stdout, 1);\n\t\t\tclose(save_stdout);\n\t\t}\n\n\t\tif (change_user(user) < 0)\n\t\t\tdie(\"Failed to change user to %s\", user);\n\n\t\texecute_program(argc, argv);\n\t}\n\n\tif (do_daemonize)\n\t\tdaemonize_finish();\n\tif (fork_process)\n\t\texit(0);\n\tif (do_ptrace) {\n\t\tptrace_attach(NULL, pid);\n\t\tptrace_wait(type);\n\t} else\n\t\ttrace_waitpid(type, pid, &status, 0);\n\tif (type & (TRACE_TYPE_START | TRACE_TYPE_SET))\n\t\texit(0);\n}\n\nstatic void\nset_plugin_instance(struct buffer_instance *instance, const char *name)\n{\n\tchar *path;\n\tchar zero = '0';\n\tint ret;\n\tint fd;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"current_tracer\");\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0) {\n\t\t/*\n\t\t * Legacy kernels do not have current_tracer file, and they\n\t\t * always use nop. So, it doesn't need to try to change the\n\t\t * plugin for those if name is \"nop\".\n\t\t */\n\t\tif (!strncmp(name, \"nop\", 3)) {\n\t\t\ttracefs_put_tracing_file(path);\n\t\t\treturn;\n\t\t}\n\t\tdie(\"Opening '%s'\", path);\n\t}\n\tret = write(fd, name, strlen(name));\n\tclose(fd);\n\n\tif (ret < 0)\n\t\tdie(\"writing to '%s'\", path);\n\n\ttracefs_put_tracing_file(path);\n\n\tif (strncmp(name, \"function\", 8) != 0)\n\t\treturn;\n\n\t/* Make sure func_stack_trace option is disabled */\n\t/* First try instance file, then top level */\n\tpath = tracefs_instance_get_file(instance->tracefs, \"options/func_stack_trace\");\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0) {\n\t\ttracefs_put_tracing_file(path);\n\t\tpath = tracefs_get_tracing_file(\"options/func_stack_trace\");\n\t\tfd = open(path, O_WRONLY);\n\t\tif (fd < 0) {\n\t\t\ttracefs_put_tracing_file(path);\n\t\t\treturn;\n\t\t}\n\t}\n\t/*\n\t * Always reset func_stack_trace to zero. Don't bother saving\n\t * the original content.\n\t */\n\tadd_reset_file(path, \"0\", RESET_HIGH_PRIO);\n\ttracefs_put_tracing_file(path);\n\twrite(fd, &zero, 1);\n\tclose(fd);\n}\n\nstatic void set_plugin(const char *name)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tset_plugin_instance(instance, name);\n}\n\nstatic void save_option(struct buffer_instance *instance, const char *option)\n{\n\tstruct opt_list *opt;\n\n\topt = malloc(sizeof(*opt));\n\tif (!opt)\n\t\tdie(\"Failed to allocate option\");\n\topt->next = instance->options;\n\tinstance->options = opt;\n\topt->option = option;\n}\n\nstatic int set_option(struct buffer_instance *instance, const char *option)\n{\n\tchar *path;\n\tint ret;\n\tint fd;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"trace_options\");\n\tfd = open(path, O_WRONLY | O_TRUNC);\n\tif (fd < 0)\n\t\twarning(\"writing to '%s'\", path);\n\ttracefs_put_tracing_file(path);\n\n\tif (fd < 0)\n\t\treturn -1;\n\n\tret = write(fd, option, strlen(option));\n\tclose(fd);\n\n\treturn ret < 0 ? ret : 0;\n}\n\nstatic void disable_func_stack_trace_instance(struct buffer_instance *instance)\n{\n\tstruct stat st;\n\tchar *content;\n\tchar *path;\n\tchar *cond;\n\tint size;\n\tint ret;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"current_tracer\");\n\tret = stat(path, &st);\n\ttracefs_put_tracing_file(path);\n\tif (ret < 0)\n\t\treturn;\n\n\tcontent = tracefs_instance_file_read(instance->tracefs,\n\t\t\t\t\t     \"current_tracer\", &size);\n\tcond = strstrip(content);\n\tif (memcmp(cond, \"function\", size - (cond - content)) !=0)\n\t\tgoto out;\n\n\tset_option(instance, \"nofunc_stack_trace\");\n out:\n\tfree(content);\n}\n\nstatic void disable_func_stack_trace(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tdisable_func_stack_trace_instance(instance);\n}\n\nstatic void add_reset_options(struct buffer_instance *instance)\n{\n\tstruct opt_list *opt;\n\tconst char *option;\n\tchar *content;\n\tchar *path;\n\tchar *ptr;\n\tint len;\n\n\tif (keep)\n\t\treturn;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"trace_options\");\n\tcontent = get_file_content(path);\n\n\tfor (opt = instance->options; opt; opt = opt->next) {\n\t\toption = opt->option;\n\t\tlen = strlen(option);\n\t\tptr = content;\n again:\n\t\tptr = strstr(ptr, option);\n\t\tif (ptr) {\n\t\t\t/* First make sure its the option we want */\n\t\t\tif (ptr[len] != '\\n') {\n\t\t\t\tptr += len;\n\t\t\t\tgoto again;\n\t\t\t}\n\t\t\tif (ptr - content >= 2 && strncmp(ptr - 2, \"no\", 2) == 0) {\n\t\t\t\t/* Make sure this isn't ohno-option */\n\t\t\t\tif (ptr > content + 2 && *(ptr - 3) != '\\n') {\n\t\t\t\t\tptr += len;\n\t\t\t\t\tgoto again;\n\t\t\t\t}\n\t\t\t\t/* we enabled it */\n\t\t\t\tptr[len] = 0;\n\t\t\t\tadd_reset_file(path, ptr-2, RESET_DEFAULT_PRIO);\n\t\t\t\tptr[len] = '\\n';\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t/* make sure this is our option */\n\t\t\tif (ptr > content && *(ptr - 1) != '\\n') {\n\t\t\t\tptr += len;\n\t\t\t\tgoto again;\n\t\t\t}\n\t\t\t/* this option hasn't changed, ignore it */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* ptr is NULL, not found, maybe option is a no */\n\t\tif (strncmp(option, \"no\", 2) != 0)\n\t\t\t/* option is really not found? */\n\t\t\tcontinue;\n\n\t\toption += 2;\n\t\tlen = strlen(option);\n\t\tptr = content;\n loop:\n\t\tptr = strstr(content, option);\n\t\tif (!ptr)\n\t\t\t/* Really not found? */\n\t\t\tcontinue;\n\n\t\t/* make sure this is our option */\n\t\tif (ptr[len] != '\\n') {\n\t\t\tptr += len;\n\t\t\tgoto loop;\n\t\t}\n\n\t\tif (ptr > content && *(ptr - 1) != '\\n') {\n\t\t\tptr += len;\n\t\t\tgoto loop;\n\t\t}\n\n\t\tadd_reset_file(path, option, RESET_DEFAULT_PRIO);\n\t}\n\ttracefs_put_tracing_file(path);\n\tfree(content);\n}\n\nstatic void set_options(void)\n{\n\tstruct buffer_instance *instance;\n\tstruct opt_list *opt;\n\tint ret;\n\n\tfor_all_instances(instance) {\n\t\tadd_reset_options(instance);\n\t\twhile (instance->options) {\n\t\t\topt = instance->options;\n\t\t\tinstance->options = opt->next;\n\t\t\tret = set_option(instance, opt->option);\n\t\t\tif (ret < 0)\n\t\t\t\tdie(\"Failed to  set ftrace option %s\",\n\t\t\t\t    opt->option);\n\t\t\tfree(opt);\n\t\t}\n\t}\n}\n\nstatic void set_saved_cmdlines_size(struct common_record_context *ctx)\n{\n\tint fd, len, ret = -1;\n\tchar *path, *str;\n\n\tif (!ctx->saved_cmdlines_size)\n\t\treturn;\n\n\tpath = tracefs_get_tracing_file(\"saved_cmdlines_size\");\n\tif (!path)\n\t\tgoto err;\n\n\treset_save_file(path, RESET_DEFAULT_PRIO);\n\n\tfd = open(path, O_WRONLY);\n\ttracefs_put_tracing_file(path);\n\tif (fd < 0)\n\t\tgoto err;\n\n\tlen = asprintf(&str, \"%d\", ctx->saved_cmdlines_size);\n\tif (len < 0)\n\t\tdie(\"%s couldn't allocate memory\", __func__);\n\n\tif (write(fd, str, len) > 0)\n\t\tret = 0;\n\n\tclose(fd);\n\tfree(str);\nerr:\n\tif (ret)\n\t\twarning(\"Couldn't set saved_cmdlines_size\");\n}\n\nstatic int trace_check_file_exists(struct buffer_instance *instance, char *file)\n{\n\tstruct stat st;\n\tchar *path;\n\tint ret;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, file);\n\tret = stat(path, &st);\n\ttracefs_put_tracing_file(path);\n\n\treturn ret < 0 ? 0 : 1;\n}\n\nstatic int use_old_event_method(void)\n{\n\tstatic int old_event_method;\n\tstatic int processed;\n\n\tif (processed)\n\t\treturn old_event_method;\n\n\t/* Check if the kernel has the events/enable file */\n\tif (!trace_check_file_exists(&top_instance, \"events/enable\"))\n\t\told_event_method = 1;\n\n\tprocessed = 1;\n\n\treturn old_event_method;\n}\n\nstatic void old_update_events(const char *name, char update)\n{\n\tchar *path;\n\tFILE *fp;\n\tint ret;\n\n\tif (strcmp(name, \"all\") == 0)\n\t\tname = \"*:*\";\n\n\t/* need to use old way */\n\tpath = tracefs_get_tracing_file(\"set_event\");\n\tfp = fopen(path, \"w\");\n\tif (!fp)\n\t\tdie(\"opening '%s'\", path);\n\ttracefs_put_tracing_file(path);\n\n\t/* Disable the event with \"!\" */\n\tif (update == '0')\n\t\tfwrite(\"!\", 1, 1, fp);\n\n\tret = fwrite(name, 1, strlen(name), fp);\n\tif (ret < 0)\n\t\tdie(\"bad event '%s'\", name);\n\n\tret = fwrite(\"\\n\", 1, 1, fp);\n\tif (ret < 0)\n\t\tdie(\"bad event '%s'\", name);\n\n\tfclose(fp);\n\n\treturn;\n}\n\nstatic void\nreset_events_instance(struct buffer_instance *instance)\n{\n\tglob_t globbuf;\n\tchar *path;\n\tchar c;\n\tint fd;\n\tint i;\n\tint ret;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tif (use_old_event_method()) {\n\t\t/* old way only had top instance */\n\t\tif (!is_top_instance(instance))\n\t\t\treturn;\n\t\told_update_events(\"all\", '0');\n\t\treturn;\n\t}\n\n\tc = '0';\n\tpath = tracefs_instance_get_file(instance->tracefs, \"events/enable\");\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0)\n\t\tdie(\"opening to '%s'\", path);\n\tret = write(fd, &c, 1);\n\tclose(fd);\n\ttracefs_put_tracing_file(path);\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"events/*/filter\");\n\tglobbuf.gl_offs = 0;\n\tret = glob(path, 0, NULL, &globbuf);\n\ttracefs_put_tracing_file(path);\n\t/* no request flags, so only GLOB_NOSPACE and GLOB_NOMATCH */\n\tif (ret != 0)\n\t\treturn;\n\n\tfor (i = 0; i < globbuf.gl_pathc; i++) {\n\t\tpath = globbuf.gl_pathv[i];\n\t\tfd = open(path, O_WRONLY);\n\t\tif (fd < 0)\n\t\t\tdie(\"opening to '%s'\", path);\n\t\tret = write(fd, &c, 1);\n\t\tclose(fd);\n\t}\n\tglobfree(&globbuf);\n}\n\nstatic void reset_events(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\treset_events_instance(instance);\n}\n\nenum {\n\tSTATE_NEWLINE,\n\tSTATE_SKIP,\n\tSTATE_COPY,\n};\n\nstatic char *read_file(const char *file)\n{\n\tchar stbuf[BUFSIZ];\n\tchar *buf = NULL;\n\tint size = 0;\n\tchar *nbuf;\n\tint fd;\n\tint r;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0)\n\t\treturn NULL;\n\n\tdo {\n\t\tr = read(fd, stbuf, BUFSIZ);\n\t\tif (r <= 0)\n\t\t\tcontinue;\n\t\tnbuf = realloc(buf, size+r+1);\n\t\tif (!nbuf) {\n\t\t\tfree(buf);\n\t\t\tbuf = NULL;\n\t\t\tbreak;\n\t\t}\n\t\tbuf = nbuf;\n\t\tmemcpy(buf+size, stbuf, r);\n\t\tsize += r;\n\t} while (r > 0);\n\n\tclose(fd);\n\tif (r == 0 && size > 0)\n\t\tbuf[size] = '\\0';\n\n\treturn buf;\n}\n\nstatic void read_error_log(const char *log)\n{\n\tchar *buf, *line;\n\tchar *start = NULL;\n\tchar *p;\n\n\tbuf = read_file(log);\n\tif (!buf)\n\t\treturn;\n\n\tline = buf;\n\n\t/* Only the last lines have meaning */\n\twhile ((p = strstr(line, \"\\n\")) && p[1]) {\n\t\tif (line[0] != ' ')\n\t\t\tstart = line;\n\t\tline = p + 1;\n\t}\n\n\tif (start)\n\t\tprintf(\"%s\", start);\n\n\tfree(buf);\n}\n\nstatic void show_error(const char *file, const char *type)\n{\n\tstruct stat st;\n\tchar *path = strdup(file);\n\tchar *p;\n\tint ret;\n\n\tif (!path)\n\t\tdie(\"Could not allocate memory\");\n\n\tp = strstr(path, \"tracing\");\n\tif (p) {\n\t\tif (strncmp(p + sizeof(\"tracing\"), \"instances\", sizeof(\"instances\") - 1) == 0) {\n\t\t\tp = strstr(p + sizeof(\"tracing\") + sizeof(\"instances\"), \"/\");\n\t\t\tif (!p)\n\t\t\t\tgoto read_file;\n\t\t} else {\n\t\t\tp += sizeof(\"tracing\") - 1;\n\t\t}\n\t\tret = asprintf(&p, \"%.*s/error_log\", (int)(p - path), path);\n\t\tif (ret < 0)\n\t\t\tdie(\"Could not allocate memory\");\n\t\tret = stat(p, &st);\n\t\tif (ret < 0) {\n\t\t\tfree(p);\n\t\t\tgoto read_file;\n\t\t}\n\t\tread_error_log(p);\n\t\tgoto out;\n\t}\n\n read_file:\n\tp = read_file(path);\n\tif (p)\n\t\tprintf(\"%s\", p);\n\n out:\n\tprintf(\"Failed %s of %s\\n\", type, file);\n\tfree(p);\n\tfree(path);\n\treturn;\n}\n\nstatic void write_filter(const char *file, const char *filter)\n{\n\tif (write_file(file, filter) < 0)\n\t\tshow_error(file, \"filter\");\n}\n\nstatic void clear_filter(const char *file)\n{\n\twrite_filter(file, \"0\");\n}\n\nstatic void write_trigger(const char *file, const char *trigger)\n{\n\tif (write_file(file, trigger) < 0)\n\t\tshow_error(file, \"trigger\");\n}\n\nstatic int clear_trigger(const char *file)\n{\n\tchar trigger[BUFSIZ];\n\tchar *save = NULL;\n\tchar *line;\n\tchar *buf;\n\tint len;\n\tint ret;\n\n\tbuf = read_file(file);\n\tif (!buf) {\n\t\tperror(file);\n\t\treturn 0;\n\t}\n\n\ttrigger[0] = '!';\n\n\tfor (line = strtok_r(buf, \"\\n\", &save); line; line = strtok_r(NULL, \"\\n\", &save)) {\n\t\tif (line[0] == '#')\n\t\t\tcontinue;\n\t\tlen = strlen(line);\n\t\tif (len > BUFSIZ - 2)\n\t\t\tlen = BUFSIZ - 2;\n\t\tstrncpy(trigger + 1, line, len);\n\t\ttrigger[len + 1] = '\\0';\n\t\t/* We don't want any filters or extra on the line */\n\t\tstrtok(trigger, \" \");\n\t\twrite_file(file, trigger);\n\t}\n\n\tfree(buf);\n\n\t/*\n\t * Some triggers have an order in removing them.\n\t * They will not be removed if done in the wrong order.\n\t */\n\tbuf = read_file(file);\n\tif (!buf)\n\t\treturn 0;\n\n\tret = 0;\n\tfor (line = strtok(buf, \"\\n\"); line; line = strtok(NULL, \"\\n\")) {\n\t\tif (line[0] == '#')\n\t\t\tcontinue;\n\t\tret = 1;\n\t\tbreak;\n\t}\n\tfree(buf);\n\treturn ret;\n}\n\nstatic void clear_func_filter(const char *file)\n{\n\tchar filter[BUFSIZ];\n\tstruct stat st;\n\tchar *line;\n\tchar *buf;\n\tchar *p;\n\tint len;\n\tint ret;\n\tint fd;\n\n\t/* Function filters may not exist */\n\tret = stat(file, &st);\n\tif (ret < 0)\n\t\treturn;\n\n\t/*  First zero out normal filters */\n\tfd = open(file, O_WRONLY | O_TRUNC);\n\tif (fd < 0)\n\t\tdie(\"opening to '%s'\", file);\n\tclose(fd);\n\n\tbuf = read_file(file);\n\tif (!buf) {\n\t\tperror(file);\n\t\treturn;\n\t}\n\n\t/* Now remove filters */\n\tfilter[0] = '!';\n\n\t/*\n\t * To delete a filter, we need to write a '!filter'\n\t * to the file for each filter.\n\t */\n\tfor (line = strtok(buf, \"\\n\"); line; line = strtok(NULL, \"\\n\")) {\n\t\tif (line[0] == '#')\n\t\t\tcontinue;\n\t\tlen = strlen(line);\n\t\tif (len > BUFSIZ - 2)\n\t\t\tlen = BUFSIZ - 2;\n\n\t\tstrncpy(filter + 1, line, len);\n\t\tfilter[len + 1] = '\\0';\n\t\t/*\n\t\t * To remove \"unlimited\" filters, we must remove\n\t\t * the \":unlimited\" from what we write.\n\t\t */\n\t\tif ((p = strstr(filter, \":unlimited\"))) {\n\t\t\t*p = '\\0';\n\t\t\tlen = p - filter;\n\t\t}\n\t\t/*\n\t\t * The write to this file expects white space\n\t\t * at the end :-p\n\t\t */\n\t\tfilter[len] = '\\n';\n\t\tfilter[len+1] = '\\0';\n\t\twrite_file(file, filter);\n\t}\n\tfree(buf);\n}\n\nstatic void update_reset_triggers(void)\n{\n\tstruct reset_file *reset;\n\n\twhile (reset_triggers) {\n\t\treset = reset_triggers;\n\t\treset_triggers = reset->next;\n\n\t\tclear_trigger(reset->path);\n\t\tfree(reset->path);\n\t\tfree(reset);\n\t}\n}\n\nstatic void reset_buffer_files_instance(struct buffer_instance *instance)\n{\n\tif (instance->old_buffer_size != instance->buffer_size)\n\t\ttracefs_instance_set_buffer_size(instance->tracefs,\n\t\t\t\t\t\t instance->old_buffer_size, -1);\n\n\tif (instance->old_subbuf_size != instance->subbuf_size)\n\t\ttracefs_instance_set_subbuf_size(instance->tracefs,\n\t\t\t\t\t\t instance->old_subbuf_size);\n}\n\nstatic void reset_buffer_files(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance) {\n\t\treset_buffer_files_instance(instance);\n\t}\n}\n\nstatic void update_reset_files(void)\n{\n\tstruct reset_file *reset;\n\n\twhile (reset_files) {\n\t\treset = reset_files;\n\t\treset_files = reset->next;\n\n\t\tif (!keep)\n\t\t\twrite_file(reset->path, reset->reset);\n\t\tfree(reset->path);\n\t\tfree(reset->reset);\n\t\tfree(reset);\n\t}\n\n\treset_buffer_files();\n}\n\nstatic void\nupdate_event(struct event_list *event, const char *filter,\n\t     int filter_only, char update)\n{\n\tconst char *name = event->event;\n\tFILE *fp;\n\tchar *path;\n\tint ret;\n\n\tif (use_old_event_method()) {\n\t\tif (filter_only)\n\t\t\treturn;\n\t\told_update_events(name, update);\n\t\treturn;\n\t}\n\n\tif (filter && event->filter_file) {\n\t\tadd_reset_file(event->filter_file, \"0\", RESET_DEFAULT_PRIO);\n\t\twrite_filter(event->filter_file, filter);\n\t}\n\n\tif (event->trigger_file) {\n\t\tadd_reset_trigger(event->trigger_file);\n\t\tclear_trigger(event->trigger_file);\n\t\twrite_trigger(event->trigger_file, event->trigger);\n\t\t/* Make sure we don't write this again */\n\t\tfree(event->trigger_file);\n\t\tfree(event->trigger);\n\t\tevent->trigger_file = NULL;\n\t\tevent->trigger = NULL;\n\t}\n\n\tif (filter_only || !event->enable_file)\n\t\treturn;\n\n\tpath = event->enable_file;\n\n\tfp = fopen(path, \"w\");\n\tif (!fp)\n\t\tdie(\"writing to '%s'\", path);\n\tret = fwrite(&update, 1, 1, fp);\n\tfclose(fp);\n\tif (ret < 0)\n\t\tdie(\"writing to '%s'\", path);\n}\n\n/*\n * The debugfs file tracing_enabled needs to be deprecated.\n * But just in case anyone fiddled with it. If it exists,\n * make sure it is one.\n * No error checking needed here.\n */\nstatic void check_tracing_enabled(void)\n{\n\tstatic int fd = -1;\n\tchar *path;\n\n\tif (fd < 0) {\n\t\tpath = tracefs_get_tracing_file(\"tracing_enabled\");\n\t\tfd = open(path, O_WRONLY | O_CLOEXEC);\n\t\ttracefs_put_tracing_file(path);\n\n\t\tif (fd < 0)\n\t\t\treturn;\n\t}\n\twrite(fd, \"1\", 1);\n}\n\nstatic int open_instance_fd(struct buffer_instance *instance,\n\t\t\t    const char *file, int flags)\n{\n\tint fd;\n\tchar *path;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, file);\n\tfd = open(path, flags);\n\tif (fd < 0) {\n\t\t/* instances may not be created yet */\n\t\tif (is_top_instance(instance))\n\t\t\tdie(\"opening '%s'\", path);\n\t}\n\ttracefs_put_tracing_file(path);\n\n\treturn fd;\n}\n\nstatic int open_tracing_on(struct buffer_instance *instance)\n{\n\tint fd = instance->tracing_on_fd;\n\n\t/* OK, we keep zero for stdin */\n\tif (fd > 0)\n\t\treturn fd;\n\n\tfd = open_instance_fd(instance, \"tracing_on\", O_RDWR | O_CLOEXEC);\n\tif (fd < 0) {\n\t\treturn fd;\n\t}\n\tinstance->tracing_on_fd = fd;\n\n\treturn fd;\n}\n\nstatic void write_tracing_on(struct buffer_instance *instance, int on)\n{\n\tint ret;\n\tint fd;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tfd = open_tracing_on(instance);\n\tif (fd < 0)\n\t\treturn;\n\n\tif (on)\n\t\tret = write(fd, \"1\", 1);\n\telse\n\t\tret = write(fd, \"0\", 1);\n\n\tif (ret < 0)\n\t\tdie(\"writing 'tracing_on'\");\n}\n\nstatic int read_tracing_on(struct buffer_instance *instance)\n{\n\tint fd;\n\tchar buf[10];\n\tint ret;\n\n\tif (is_guest(instance))\n\t\treturn -1;\n\n\tfd = open_tracing_on(instance);\n\tif (fd < 0)\n\t\treturn fd;\n\n\tret = read(fd, buf, 10);\n\tif (ret <= 0)\n\t\tdie(\"Reading 'tracing_on'\");\n\tbuf[9] = 0;\n\tret = atoi(buf);\n\n\treturn ret;\n}\n\nstatic void reset_max_latency_instance(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\treset_max_latency(instance);\n}\n\nvoid tracecmd_enable_tracing(void)\n{\n\tstruct buffer_instance *instance;\n\n\tcheck_tracing_enabled();\n\n\tfor_all_instances(instance)\n\t\twrite_tracing_on(instance, 1);\n\n\tif (latency)\n\t\treset_max_latency_instance();\n}\n\nvoid tracecmd_disable_tracing(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\twrite_tracing_on(instance, 0);\n}\n\nvoid tracecmd_disable_all_tracing(int disable_tracer)\n{\n\tstruct buffer_instance *instance;\n\n\ttracecmd_disable_tracing();\n\n\tdisable_func_stack_trace();\n\n\tif (disable_tracer)\n\t\tset_plugin(\"nop\");\n\n\treset_events();\n\n\t/* Force close and reset of ftrace pid file */\n\tfor_all_instances(instance)\n\t\tupdate_ftrace_pid(instance, \"\", 1);\n\n\tclear_trace_instances();\n}\n\nstatic void\nupdate_sched_event(struct buffer_instance *instance,\n\t\t   struct event_list *event, const char *field)\n{\n\tif (!event)\n\t\treturn;\n\n\tevent->pid_filter = make_pid_filter(instance, event->pid_filter, field);\n}\n\nstatic void update_event_filters(struct buffer_instance *instance)\n{\n\tstruct event_list *event;\n\tchar *event_filter;\n\tint free_it;\n\tint len;\n\tint common_len = 0;\n\n\tif (instance->common_pid_filter)\n\t\tcommon_len = strlen(instance->common_pid_filter);\n\n\tfor (event = instance->events; event; event = event->next) {\n\t\tif (!event->neg) {\n\n\t\t\tfree_it = 0;\n\t\t\tif (event->filter) {\n\t\t\t\tif (!instance->common_pid_filter)\n\t\t\t\t\t/*\n\t\t\t\t\t * event->pid_filter is only created if\n\t\t\t\t\t * common_pid_filter is. No need to check that.\n\t\t\t\t\t * Just use the current event->filter.\n\t\t\t\t\t */\n\t\t\t\t\tevent_filter = event->filter;\n\t\t\t\telse if (event->pid_filter) {\n\t\t\t\t\tfree_it = 1;\n\t\t\t\t\tlen = common_len + strlen(event->pid_filter) +\n\t\t\t\t\t\tstrlen(event->filter) + strlen(\"()&&(||)\") + 1;\n\t\t\t\t\tevent_filter = malloc(len);\n\t\t\t\t\tif (!event_filter)\n\t\t\t\t\t\tdie(\"Failed to allocate event_filter\");\n\t\t\t\t\tsprintf(event_filter, \"(%s)&&(%s||%s)\",\n\t\t\t\t\t\tevent->filter, instance->common_pid_filter,\n\t\t\t\t\t\tevent->pid_filter);\n\t\t\t\t} else {\n\t\t\t\t\tfree_it = 1;\n\t\t\t\t\tlen = common_len + strlen(event->filter) +\n\t\t\t\t\t\tstrlen(\"()&&()\") + 1;\n\t\t\t\t\tevent_filter = malloc(len);\n\t\t\t\t\tif (!event_filter)\n\t\t\t\t\t\tdie(\"Failed to allocate event_filter\");\n\t\t\t\t\tsprintf(event_filter, \"(%s)&&(%s)\",\n\t\t\t\t\t\tevent->filter, instance->common_pid_filter);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t/* event->pid_filter only exists when common_pid_filter does */\n\t\t\t\tif (!instance->common_pid_filter)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (event->pid_filter) {\n\t\t\t\t\tfree_it = 1;\n\t\t\t\t\tlen = common_len + strlen(event->pid_filter) +\n\t\t\t\t\t\tstrlen(\"||\") + 1;\n\t\t\t\t\tevent_filter = malloc(len);\n\t\t\t\t\tif (!event_filter)\n\t\t\t\t\t\tdie(\"Failed to allocate event_filter\");\n\t\t\t\t\tsprintf(event_filter, \"%s||%s\",\n\t\t\t\t\t\t\tinstance->common_pid_filter, event->pid_filter);\n\t\t\t\t} else\n\t\t\t\t\tevent_filter = instance->common_pid_filter;\n\t\t\t}\n\n\t\t\tupdate_event(event, event_filter, 1, '1');\n\t\t\tif (free_it)\n\t\t\t\tfree(event_filter);\n\t\t}\n\t}\n}\n\nstatic void update_pid_filters(struct buffer_instance *instance)\n{\n\tstruct filter_pids *p;\n\tchar *filter;\n\tchar *str;\n\tint len;\n\tint ret;\n\tint fd;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tfd = open_instance_fd(instance, \"set_event_pid\",\n\t\t\t      O_WRONLY | O_CLOEXEC | O_TRUNC);\n\tif (fd < 0)\n\t\tdie(\"Failed to access set_event_pid\");\n\n\tlen = instance->len_filter_pids + instance->nr_filter_pids + 1;\n\tfilter = malloc(len);\n\tif (!filter)\n\t\tdie(\"Failed to allocate pid filter\");\n\n\tstr = filter;\n\n\tfor (p = instance->filter_pids; p; p = p->next) {\n\t\tif (p->exclude)\n\t\t\tcontinue;\n\t\tlen = sprintf(str, \"%d \", p->pid);\n\t\tstr += len;\n\t}\n\n\tif (filter == str)\n\t\tgoto out;\n\n\tlen = str - filter;\n\tstr = filter;\n\tdo {\n\t\tret = write(fd, str, len);\n\t\tif (ret < 0)\n\t\t\tdie(\"Failed to write to set_event_pid\");\n\t\tstr += ret;\n\t\tlen -= ret;\n\t} while (ret >= 0 && len);\n\n out:\n\tfree(filter);\n\tclose(fd);\n}\n\nstatic void update_pid_event_filters(struct buffer_instance *instance)\n{\n\tif (instance->have_set_event_pid)\n\t\treturn update_pid_filters(instance);\n\t/*\n\t * Also make sure that the sched_switch to this pid\n\t * and wakeups of this pid are also traced.\n\t * Only need to do this if the events are active.\n\t */\n\tupdate_sched_event(instance, instance->sched_switch_event, \"next_pid\");\n\tupdate_sched_event(instance, instance->sched_wakeup_event, \"pid\");\n\tupdate_sched_event(instance, instance->sched_wakeup_new_event, \"pid\");\n\n\tupdate_event_filters(instance);\n}\n\n#define MASK_STR_MAX 4096 /* Don't expect more than 32768 CPUS */\n\nstatic char *alloc_mask_from_hex(struct buffer_instance *instance, const char *str)\n{\n\tchar *cpumask;\n\n\tif (strcmp(str, \"-1\") == 0) {\n\t\t/* set all CPUs */\n\t\tint bytes = (instance->cpu_count + 7) / 8;\n\t\tint last = instance->cpu_count % 8;\n\t\tint i;\n\n\t\tcpumask = malloc(MASK_STR_MAX);\n\t\tif (!cpumask)\n\t\t\tdie(\"can't allocate cpumask\");\n\n\t\tif (bytes > (MASK_STR_MAX-1)) {\n\t\t\twarning(\"cpumask can't handle more than 32768 CPUS!\");\n\t\t\tbytes = MASK_STR_MAX-1;\n\t\t}\n\n\t\tsprintf(cpumask, \"%x\", (1 << last) - 1);\n\n\t\tfor (i = 1; i < bytes; i++)\n\t\t\tcpumask[i] = 'f';\n\n\t\tcpumask[i+1] = 0;\n\t} else {\n\t\tcpumask = strdup(str);\n\t\tif (!cpumask)\n\t\t\tdie(\"can't allocate cpumask\");\n\t}\n\n\treturn cpumask;\n}\n\nstatic void set_mask(struct buffer_instance *instance)\n{\n\tstruct stat st;\n\tchar *path;\n\tint fd;\n\tint ret;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tif (!instance->cpumask)\n\t\treturn;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"tracing_cpumask\");\n\tif (!path)\n\t\tdie(\"could not allocate path\");\n\treset_save_file(path, RESET_DEFAULT_PRIO);\n\n\tret = stat(path, &st);\n\tif (ret < 0) {\n\t\twarning(\"%s not found\", path);\n\t\tgoto out;\n\t}\n\n\tfd = open(path, O_WRONLY | O_TRUNC);\n\tif (fd < 0)\n\t\tdie(\"could not open %s\\n\", path);\n\n\twrite(fd, instance->cpumask, strlen(instance->cpumask));\n\n\tclose(fd);\n out:\n\ttracefs_put_tracing_file(path);\n\tfree(instance->cpumask);\n\tinstance->cpumask = NULL;\n}\n\nstatic void enable_events(struct buffer_instance *instance)\n{\n\tstruct event_list *event;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tfor (event = instance->events; event; event = event->next) {\n\t\tif (!event->neg)\n\t\t\tupdate_event(event, event->filter, 0, '1');\n\t}\n\n\t/* Now disable any events */\n\tfor (event = instance->events; event; event = event->next) {\n\t\tif (event->neg)\n\t\t\tupdate_event(event, NULL, 0, '0');\n\t}\n}\n\nvoid tracecmd_enable_events(void)\n{\n\tenable_events(first_instance);\n}\n\nstatic void set_clock(struct common_record_context *ctx, struct buffer_instance *instance)\n{\n\tconst char *clock;\n\tchar *path;\n\tchar *content;\n\tchar *str;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tif (instance->clock)\n\t\tclock = instance->clock;\n\telse\n\t\tclock = ctx->clock;\n\n\tif (!clock)\n\t\treturn;\n\n\t/* The current clock is in brackets, reset it when we are done */\n\tcontent = tracefs_instance_file_read(instance->tracefs,\n\t\t\t\t\t     \"trace_clock\", NULL);\n\n\t/* check if first clock is set */\n\tif (*content == '[')\n\t\tstr = strtok(content+1, \"]\");\n\telse {\n\t\tstr = strtok(content, \"[\");\n\t\tif (!str)\n\t\t\tdie(\"Can not find clock in trace_clock\");\n\t\tstr = strtok(NULL, \"]\");\n\t}\n\tpath = tracefs_instance_get_file(instance->tracefs, \"trace_clock\");\n\tadd_reset_file(path, str, RESET_DEFAULT_PRIO);\n\n\tfree(content);\n\ttracefs_put_tracing_file(path);\n\n\ttracefs_instance_file_write(instance->tracefs,\n\t\t\t\t    \"trace_clock\", clock);\n}\n\nstatic void set_max_graph_depth(struct buffer_instance *instance, char *max_graph_depth)\n{\n\tchar *path;\n\tint ret;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"max_graph_depth\");\n\treset_save_file(path, RESET_DEFAULT_PRIO);\n\ttracefs_put_tracing_file(path);\n\tret = tracefs_instance_file_write(instance->tracefs, \"max_graph_depth\",\n\t\t\t\t\t  max_graph_depth);\n\tif (ret < 0)\n\t\tdie(\"could not write to max_graph_depth\");\n}\n\nstatic bool check_file_in_dir(char *dir, char *file)\n{\n\tstruct stat st;\n\tchar *path;\n\tint ret;\n\n\tret = asprintf(&path, \"%s/%s\", dir, file);\n\tif (ret < 0)\n\t\tdie(\"Failed to allocate id file path for %s/%s\", dir, file);\n\tret = stat(path, &st);\n\tfree(path);\n\tif (ret < 0 || S_ISDIR(st.st_mode))\n\t\treturn false;\n\treturn true;\n}\n\n/**\n * create_event - create and event descriptor\n * @instance: instance to use\n * @path: path to event attribute\n * @old_event: event descriptor to use as base\n *\n * NOTE: the function purpose is to create a data structure to describe\n * an ftrace event. During the process it becomes handy to change the\n * string `path`. So, do not rely on the content of `path` after you\n * invoke this function.\n */\nstatic struct event_list *\ncreate_event(struct buffer_instance *instance, char *path, struct event_list *old_event)\n{\n\tstruct event_list *event;\n\tstruct stat st;\n\tchar *path_dirname;\n\tchar *p;\n\tint ret;\n\n\tevent = malloc(sizeof(*event));\n\tif (!event)\n\t\tdie(\"Failed to allocate event\");\n\t*event = *old_event;\n\tadd_event(instance, event);\n\n\tif (event->filter || filter_task || instance->filter_pids) {\n\t\tevent->filter_file = strdup(path);\n\t\tif (!event->filter_file)\n\t\t\tdie(\"malloc filter file\");\n\t}\n\n\tpath_dirname = dirname(path);\n\n\tret = asprintf(&p, \"%s/enable\", path_dirname);\n\tif (ret < 0)\n\t\tdie(\"Failed to allocate enable path for %s\", path);\n\tret = stat(p, &st);\n\tif (ret >= 0)\n\t\tevent->enable_file = p;\n\telse\n\t\tfree(p);\n\n\tif (old_event->trigger) {\n\t\tif (check_file_in_dir(path_dirname, \"trigger\")) {\n\t\t\tevent->trigger = strdup(old_event->trigger);\n\t\t\tret = asprintf(&p, \"%s/trigger\", path_dirname);\n\t\t\tif (ret < 0)\n\t\t\t\tdie(\"Failed to allocate trigger path for %s\", path);\n\t\t\tevent->trigger_file = p;\n\t\t} else {\n\t\t\t/* Check if this is event or system.\n\t\t\t * Systems do not have trigger files by design\n\t\t\t */\n\t\t\tif (check_file_in_dir(path_dirname, \"id\"))\n\t\t\t\tdie(\"trigger specified but not supported by this kernel\");\n\t\t}\n\t}\n\n\treturn event;\n}\n\nstatic void make_sched_event(struct buffer_instance *instance,\n\t\t\t     struct event_list **event, struct event_list *sched,\n\t\t\t     const char *sched_path)\n{\n\tchar *path_dirname;\n\tchar *tmp_file;\n\tchar *path;\n\tint ret;\n\n\t/* Do nothing if the event already exists */\n\tif (*event)\n\t\treturn;\n\n\t/* we do not want to corrupt sched->filter_file when using dirname() */\n\ttmp_file = strdup(sched->filter_file);\n\tif (!tmp_file)\n\t\tdie(\"Failed to allocate path for %s\", sched_path);\n\tpath_dirname = dirname(tmp_file);\n\n\tret = asprintf(&path, \"%s/%s/filter\", path_dirname, sched_path);\n\tfree(tmp_file);\n\tif (ret < 0)\n\t\tdie(\"Failed to allocate path for %s\", sched_path);\n\n\t*event = create_event(instance, path, sched);\n\tfree(path);\n}\n\nstatic void test_event(struct event_list *event, const char *path,\n\t\t       const char *name, struct event_list **save, int len)\n{\n\tpath += len - strlen(name);\n\n\tif (strcmp(path, name) != 0)\n\t\treturn;\n\n\t*save = event;\n}\n\nstatic void print_event(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!show_status)\n\t\treturn;\n\n\tva_start(ap, fmt);\n\tvprintf(fmt, ap);\n\tva_end(ap);\n\n\tprintf(\"\\n\");\n}\n\n\nstatic int expand_event_files(struct buffer_instance *instance,\n\t\t\t      const char *file, struct event_list *old_event)\n{\n\tstruct event_list **save_event_tail = instance->event_next;\n\tstruct event_list *sched_event = NULL;\n\tstruct event_list *event;\n\tglob_t globbuf;\n\tchar *path;\n\tchar *p;\n\tint ret;\n\tint i;\n\n\tret = asprintf(&p, \"events/%s/filter\", file);\n\tif (ret < 0)\n\t\tdie(\"Failed to allocate event filter path for %s\", file);\n\n\tpath = tracefs_instance_get_file(instance->tracefs, p);\n\n\tglobbuf.gl_offs = 0;\n\tret = glob(path, 0, NULL, &globbuf);\n\ttracefs_put_tracing_file(path);\n\tfree(p);\n\n\t/* no request flags, so only GLOB_NOSPACE and GLOB_NOMATCH */\n\tif (ret != 0)\n\t\treturn 1;\n\n\tfor (i = 0; i < globbuf.gl_pathc; i++) {\n\t\tint len;\n\n\t\tpath = globbuf.gl_pathv[i];\n\n\t\tevent = create_event(instance, path, old_event);\n\t\tprint_event(\"%s\\n\", path);\n\n\t\tlen = strlen(path);\n\n\t\ttest_event(event, path, \"sched\", &sched_event, len);\n\t\ttest_event(event, path, \"sched/sched_switch\", &instance->sched_switch_event, len);\n\t\ttest_event(event, path, \"sched/sched_wakeup_new\", &instance->sched_wakeup_new_event, len);\n\t\ttest_event(event, path, \"sched/sched_wakeup\", &instance->sched_wakeup_event, len);\n\t}\n\n\tif (sched_event && sched_event->filter_file) {\n\t\t/* make sure all sched events exist */\n\t\tmake_sched_event(instance, &instance->sched_switch_event,\n\t\t\t\t sched_event, \"sched_switch\");\n\t\tmake_sched_event(instance, &instance->sched_wakeup_event,\n\t\t\t\t sched_event, \"sched_wakeup\");\n\t\tmake_sched_event(instance, &instance->sched_wakeup_new_event,\n\t\t\t\t sched_event, \"sched_wakeup_new\");\n\n\t}\n\n\n\tglobfree(&globbuf);\n\n\t/* If the event list tail changed, that means events were added */\n\treturn save_event_tail == instance->event_next;\n}\n\nstatic int expand_events_all(struct buffer_instance *instance,\n\t\t\t     char *system_name, char *event_name,\n\t\t\t     struct event_list *event)\n{\n\tchar *name;\n\tint ret;\n\n\tret = asprintf(&name, \"%s/%s\", system_name, event_name);\n\tif (ret < 0)\n\t\tdie(\"Failed to allocate system/event for %s/%s\",\n\t\t     system_name, event_name);\n\tret = expand_event_files(instance, name, event);\n\tfree(name);\n\n\treturn ret;\n}\n\nstatic void expand_event(struct buffer_instance *instance, struct event_list *event)\n{\n\tconst char *name = event->event;\n\tchar *str;\n\tchar *ptr;\n\tint ret;\n\n\t/*\n\t * We allow the user to use \"all\" to enable all events.\n\t * Expand event_selection to all systems.\n\t */\n\tif (strcmp(name, \"all\") == 0) {\n\t\texpand_event_files(instance, \"*\", event);\n\t\treturn;\n\t}\n\n\tstr = strdup(name);\n\tif (!str)\n\t\tdie(\"Failed to allocate %s string\", name);\n\n\tptr = strchr(str, ':');\n\tif (ptr) {\n\t\t*ptr = '\\0';\n\t\tptr++;\n\n\t\tif (strlen(ptr))\n\t\t\tret = expand_events_all(instance, str, ptr, event);\n\t\telse\n\t\t\tret = expand_events_all(instance, str, \"*\", event);\n\n\t\tif (!ignore_event_not_found && ret)\n\t\t\tdie(\"No events enabled with %s\", name);\n\n\t\tgoto out;\n\t}\n\n\t/* No ':' so enable all matching systems and events */\n\tret = expand_event_files(instance, str, event);\n\tret &= expand_events_all(instance, \"*\", str, event);\n\tif (event->trigger)\n\t\tret &= expand_events_all(instance, str, \"*\", event);\n\n\tif (!ignore_event_not_found && ret)\n\t\tdie(\"No events enabled with %s\", name);\n\nout:\n\tfree(str);\n}\n\nstatic void expand_event_instance(struct buffer_instance *instance)\n{\n\tstruct event_list *compressed_list = instance->events;\n\tstruct event_list *event;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\treset_event_list(instance);\n\n\twhile (compressed_list) {\n\t\tevent = compressed_list;\n\t\tcompressed_list = event->next;\n\t\texpand_event(instance, event);\n\t\tfree(event->trigger);\n\t\tfree(event);\n\t}\n}\n\nstatic void expand_event_list(void)\n{\n\tstruct buffer_instance *instance;\n\n\tif (use_old_event_method())\n\t\treturn;\n\n\tfor_all_instances(instance)\n\t\texpand_event_instance(instance);\n}\n\nstatic void finish(void)\n{\n\tstatic int secs = 1;\n\n\tsleep_time = 0;\n\n\t/* all done */\n\tif (recorder) {\n\t\ttracecmd_stop_recording(recorder);\n\t\t/*\n\t\t * We could just call the alarm if the above returned non zero,\n\t\t * as zero is suppose to guarantee that the reader woke up.\n\t\t * But as this is called from a signal handler, that may not\n\t\t * necessarily be the case.\n\t\t */\n\t\talarm(secs++);\n\t}\n\tfinished = 1;\n}\n\nstatic void flush(void)\n{\n\tif (recorder)\n\t\ttracecmd_flush_recording(recorder, false);\n}\n\nstatic void do_sig(int sig)\n{\n\tswitch (sig) {\n\tcase SIGALRM:\n\tcase SIGUSR1:\n\tcase SIGINT:\n\tcase SIGTERM:\n\t\treturn finish();\n\tcase SIGUSR2:\n\t\treturn flush();\n\t}\n}\n\nstatic struct addrinfo *do_getaddrinfo(const char *host, unsigned int port,\n\t\t\t\t       enum port_type type)\n{\n\tstruct addrinfo *results;\n\tstruct addrinfo hints;\n\tchar buf[BUFSIZ];\n\tint s;\n\n\tsnprintf(buf, BUFSIZ, \"%u\", port);\n\n\tmemset(&hints, 0, sizeof(hints));\n\thints.ai_family = AF_UNSPEC;\n\thints.ai_socktype = type == USE_TCP ? SOCK_STREAM : SOCK_DGRAM;\n\n\ts = getaddrinfo(host, buf, &hints, &results);\n\tif (s != 0) {\n\t\tgai_err = gai_strerror(s);\n\t\treturn NULL;\n\t}\n\n\tdprint(\"Attached port %s: %d to results: %p\\n\",\n\t       type == USE_TCP ? \"TCP\" : \"UDP\", port, results);\n\n\treturn results;\n}\n\nstatic int connect_addr(struct addrinfo *results)\n{\n\tstruct addrinfo *rp;\n\tint sfd = -1;\n\n\tfor (rp = results; rp != NULL; rp = rp->ai_next) {\n\t\tsfd = socket(rp->ai_family, rp->ai_socktype,\n\t\t\t     rp->ai_protocol);\n\t\tif (sfd == -1)\n\t\t\tcontinue;\n\n\t\tset_tcp_no_delay(sfd, rp->ai_socktype);\n\t\tif (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)\n\t\t\tbreak;\n\t\tclose(sfd);\n\t}\n\n\tif (rp == NULL)\n\t\treturn -1;\n\n\tdprint(\"connect results: %p with fd: %d\\n\", results, sfd);\n\n\treturn sfd;\n}\n\nstatic int connect_port(const char *host, unsigned int port, enum port_type type)\n{\n\tstruct addrinfo *results;\n\tint sfd;\n\n\tif (type == USE_VSOCK)\n\t\treturn tcmd_vsock_open(atoi(host), port);\n\n\tresults = do_getaddrinfo(host, port, type);\n\n\tif (!results)\n\t\tdie(\"connecting to %s server %s:%u\",\n\t\t    type == USE_TCP ? \"TCP\" : \"UDP\", host, port);\n\n\tsfd = connect_addr(results);\n\n\tfreeaddrinfo(results);\n\n\tif (sfd < 0)\n\t\tdie(\"Can not connect to %s server %s:%u\",\n\t\t    type == USE_TCP ? \"TCP\" : \"UDP\", host, port);\n\n\treturn sfd;\n}\n\nstatic int do_accept(int sd)\n{\n\tint cd;\n\n\tfor (;;) {\n\t\tdprint(\"Wait on accept: %d\\n\", sd);\n\t\tcd = accept(sd, NULL, NULL);\n\t\tdprint(\"accepted: %d\\n\", cd);\n\t\tif (cd < 0) {\n\t\t\tif (errno == EINTR)\n\t\t\t\tcontinue;\n\t\t\tdie(\"accept\");\n\t\t}\n\n\t\treturn cd;\n\t}\n\n\treturn -1;\n}\n\nstatic char *parse_guest_name(char *gname, int *cid, int *port,\n\t\t\t      struct addrinfo **res)\n{\n\tstruct trace_guest *guest = NULL;\n\tstruct addrinfo *result;\n\tchar *ip = NULL;\n\tchar *p;\n\n\t*res = NULL;\n\n\t*port = -1;\n\tfor (p = gname + strlen(gname); p > gname; p--) {\n\t\tif (*p == ':')\n\t\t\tbreak;\n\t}\n\tif (p > gname) {\n\t\t*p = '\\0';\n\t\t*port = atoi(p + 1);\n\t}\n\n\t*cid = -1;\n\tp = strrchr(gname, '@');\n\tif (p) {\n\t\t*p = '\\0';\n\t\t*cid = atoi(p + 1);\n\t} else if (is_digits(gname)) {\n\t\t*cid = atoi(gname);\n\t} else {\n\t\t/* Check if this is an IP address */\n\t\tif (strstr(gname, \":\") || strstr(gname, \".\"))\n\t\t\tip = gname;\n\t}\n\n\tif (!ip && *cid < 0)\n\t\tread_qemu_guests();\n\n\tif (!ip)\n\t\tguest = trace_get_guest(*cid, gname);\n\tif (guest) {\n\t\t*cid = guest->cid;\n\t\treturn guest->name;\n\t}\n\n\t/* Test to see if this is an internet address */\n\tresult = do_getaddrinfo(gname, *port, USE_TCP);\n\tif (!result)\n\t\treturn NULL;\n\n\t*res = result;\n\n\treturn gname;\n}\n\nstatic void set_prio(int prio)\n{\n\tstruct sched_param sp;\n\n\tmemset(&sp, 0, sizeof(sp));\n\tsp.sched_priority = prio;\n\tif (sched_setscheduler(0, SCHED_FIFO, &sp) < 0)\n\t\twarning(\"failed to set priority\");\n}\n\nstatic struct tracecmd_recorder *\ncreate_recorder_instance_pipe(struct buffer_instance *instance,\n\t\t\t      int cpu, int *brass)\n{\n\tstruct tracecmd_recorder *recorder;\n\tunsigned flags = recorder_flags | TRACECMD_RECORD_BLOCK_SPLICE;\n\n\t/* This is already the child */\n\tclose(brass[0]);\n\n\trecorder = tracecmd_create_buffer_recorder_fd(brass[1], cpu, flags, instance->tracefs);\n\n\treturn recorder;\n}\n\nstatic struct tracecmd_recorder *\ncreate_recorder_instance(struct buffer_instance *instance, const char *file, int cpu,\n\t\t\t int *brass)\n{\n\tstruct tracecmd_recorder *record;\n\tstruct addrinfo *result;\n\n\tif (is_guest(instance)) {\n\t\tint fd;\n\t\tunsigned int flags;\n\n\t\tif (instance->use_fifos)\n\t\t\tfd = instance->fds[cpu];\n\t\telse if (is_network(instance)) {\n\t\t\tresult = do_getaddrinfo(instance->name,\n\t\t\t\t\t\tinstance->client_ports[cpu],\n\t\t\t\t\t\tinstance->port_type);\n\t\t\tif (!result)\n\t\t\t\tdie(\"Failed to connect to %s port %d\\n\",\n\t\t\t\t    instance->name,\n\t\t\t\t    instance->client_ports[cpu]);\n\t\t\tfd = connect_addr(result);\n\t\t\tfreeaddrinfo(result);\n\t\t} else\n\t\t\tfd = tcmd_vsock_open(instance->cid, instance->client_ports[cpu]);\n\t\tif (fd < 0)\n\t\t\tdie(\"Failed to connect to agent\");\n\n\t\tflags = recorder_flags;\n\t\tif (instance->use_fifos)\n\t\t\tflags |= TRACECMD_RECORD_NOBRASS;\n\t\telse if (!tcmd_vsock_can_splice_read())\n\t\t\tflags |= TRACECMD_RECORD_NOSPLICE;\n\t\treturn tracecmd_create_recorder_virt(file, cpu, flags, fd, max_kb);\n\t}\n\n\tif (brass)\n\t\treturn create_recorder_instance_pipe(instance, cpu, brass);\n\n\tif (!tracefs_instance_get_name(instance->tracefs))\n\t\treturn tracecmd_create_recorder_maxkb(file, cpu, recorder_flags, max_kb);\n\n\trecord = tracecmd_create_buffer_recorder_maxkb(file, cpu, recorder_flags,\n\t\t\t\t\t\t       instance->tracefs, max_kb);\n\treturn record;\n}\n\n/*\n * If extract is set, then this is going to set up the recorder,\n * connections and exit as the tracing is serialized by a single thread.\n */\nstatic int create_recorder(struct buffer_instance *instance, int cpu,\n\t\t\t   enum trace_type type, int *brass)\n{\n\tstruct tracefs_instance *recorder_instance = NULL;\n\tlong ret;\n\tchar *file;\n\tpid_t pid;\n\n\tif (type != TRACE_TYPE_EXTRACT) {\n\n\t\tpid = fork();\n\t\tif (pid < 0)\n\t\t\tdie(\"fork\");\n\n\t\tif (pid)\n\t\t\treturn pid;\n\n\t\tsignal(SIGINT, SIG_IGN);\n\t\tsignal(SIGTERM, SIG_IGN);\n\t\tsignal(SIGUSR1, do_sig);\n\t\tsignal(SIGUSR2, do_sig);\n\t\tsignal(SIGALRM, do_sig);\n\n\t\tif (rt_prio)\n\t\t\tset_prio(rt_prio);\n\n\t\t/* do not kill tasks on error */\n\t\tinstance->cpu_count = 0;\n\t}\n\n\tif ((instance->client_ports && !is_guest(instance)) || is_agent(instance)) {\n\t\tunsigned int flags = recorder_flags;\n\t\tint fd;\n\n\t\tif (is_agent(instance)) {\n\t\t\tif (instance->use_fifos)\n\t\t\t\tfd = instance->fds[cpu];\n\t\t\telse {\n again:\n\t\t\t\tfd = do_accept(instance->fds[cpu]);\n\t\t\t\tif (instance->host &&\n\t\t\t\t    !trace_net_cmp_connection_fd(fd, instance->host)) {\n\t\t\t\t\tdprint(\"Client does not match '%s' for cpu:%d\\n\",\n\t\t\t\t\t       instance->host, cpu);\n\t\t\t\t\tclose(fd);\n\t\t\t\t\tgoto again;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfd = connect_port(host, instance->client_ports[cpu],\n\t\t\t\t\t  instance->port_type);\n\t\t}\n\t\tif (fd < 0)\n\t\t\tdie(\"Failed connecting to client\");\n\t\tif (tracefs_instance_get_name(instance->tracefs) && !is_agent(instance))\n\t\t\trecorder_instance = instance->tracefs;\n\n\t\trecorder = tracecmd_create_buffer_recorder_fd(fd, cpu, flags, recorder_instance);\n\t} else {\n\t\tfile = get_temp_file(instance, cpu);\n\t\trecorder = create_recorder_instance(instance, file, cpu, brass);\n\t\tput_temp_file(file);\n\t}\n\n\tif (!recorder)\n\t\tdie (\"can't create recorder\");\n\n\tif (type == TRACE_TYPE_EXTRACT) {\n\t\tret = tracecmd_flush_recording(recorder, true);\n\t\ttracecmd_free_recorder(recorder);\n\t\trecorder = NULL;\n\t\treturn ret;\n\t}\n\n\twhile (!finished) {\n\t\tif (tracecmd_start_recording(recorder, sleep_time) < 0)\n\t\t\tbreak;\n\t}\n\ttracecmd_free_recorder(recorder);\n\trecorder = NULL;\n\n\texit(0);\n}\n\nstatic void check_first_msg_from_server(struct tracecmd_msg_handle *msg_handle)\n{\n\tchar buf[BUFSIZ];\n\n\tread(msg_handle->fd, buf, 8);\n\n\t/* Make sure the server is the tracecmd server */\n\tif (memcmp(buf, \"tracecmd\", 8) != 0)\n\t\tdie(\"server not tracecmd server\");\n}\n\nstatic void communicate_with_listener_v1(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t\t struct buffer_instance *instance)\n{\n\tunsigned int *client_ports;\n\tchar buf[BUFSIZ];\n\tssize_t n;\n\tint cpu, i;\n\n\tcheck_first_msg_from_server(msg_handle);\n\n\t/* write the number of CPUs we have (in ASCII) */\n\tsprintf(buf, \"%d\", local_cpu_count);\n\n\t/* include \\0 */\n\twrite(msg_handle->fd, buf, strlen(buf)+1);\n\n\t/* write the pagesize (in ASCII) */\n\tsprintf(buf, \"%d\", page_size);\n\n\t/* include \\0 */\n\twrite(msg_handle->fd, buf, strlen(buf)+1);\n\n\t/*\n\t * If we are using IPV4 and our page size is greater than\n\t * or equal to 64K, we need to punt and use TCP. :-(\n\t */\n\n\t/* TODO, test for ipv4 */\n\tif (page_size >= UDP_MAX_PACKET) {\n\t\twarning(\"page size too big for UDP using TCP in live read\");\n\t\tinstance->port_type = USE_TCP;\n\t\tmsg_handle->flags |= TRACECMD_MSG_FL_USE_TCP;\n\t}\n\n\tif (instance->port_type == USE_TCP) {\n\t\t/* Send one option */\n\t\twrite(msg_handle->fd, \"1\", 2);\n\t\t/* Size 4 */\n\t\twrite(msg_handle->fd, \"4\", 2);\n\t\t/* use TCP */\n\t\twrite(msg_handle->fd, \"TCP\", 4);\n\t} else\n\t\t/* No options */\n\t\twrite(msg_handle->fd, \"0\", 2);\n\n\tclient_ports = malloc(local_cpu_count * sizeof(*client_ports));\n\tif (!client_ports)\n\t\tdie(\"Failed to allocate client ports for %d cpus\", local_cpu_count);\n\n\t/*\n\t * Now we will receive back a comma deliminated list\n\t * of client ports to connect to.\n\t */\n\tfor (cpu = 0; cpu < local_cpu_count; cpu++) {\n\t\tfor (i = 0; i < BUFSIZ; i++) {\n\t\t\tn = read(msg_handle->fd, buf+i, 1);\n\t\t\tif (n != 1)\n\t\t\t\tdie(\"Error, reading server ports\");\n\t\t\tif (!buf[i] || buf[i] == ',')\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == BUFSIZ)\n\t\t\tdie(\"read bad port number\");\n\t\tbuf[i] = 0;\n\t\tclient_ports[cpu] = atoi(buf);\n\t}\n\n\tinstance->client_ports = client_ports;\n}\n\nstatic void communicate_with_listener_v3(struct tracecmd_msg_handle *msg_handle,\n\t\t\t\t\t unsigned int **client_ports)\n{\n\tif (tracecmd_msg_send_init_data(msg_handle, client_ports) < 0)\n\t\tdie(\"Cannot communicate with server\");\n}\n\nstatic void check_protocol_version(struct tracecmd_msg_handle *msg_handle)\n{\n\tchar buf[BUFSIZ];\n\tint fd = msg_handle->fd;\n\tint n;\n\n\tcheck_first_msg_from_server(msg_handle);\n\n\t/*\n\t * Write the protocol version, the magic number, and the dummy\n\t * option(0) (in ASCII). The client understands whether the client\n\t * uses the v3 protocol or not by checking a reply message from the\n\t * server. If the message is \"V3\", the server uses v3 protocol. On the\n\t * other hands, if the message is just number strings, the server\n\t * returned port numbers. So, in that time, the client understands the\n\t * server uses the v1 protocol. However, the old server tells the\n\t * client port numbers after reading cpu_count, page_size, and option.\n\t * So, we add the dummy number (the magic number and 0 option) to the\n\t * first client message.\n\t */\n\twrite(fd, V3_CPU, sizeof(V3_CPU));\n\n\tbuf[0] = 0;\n\n\t/* read a reply message */\n\tn = read(fd, buf, BUFSIZ);\n\n\tif (n < 0 || !buf[0]) {\n\t\t/* the server uses the v1 protocol, so we'll use it */\n\t\tmsg_handle->version = V1_PROTOCOL;\n\t\ttracecmd_plog(\"Use the v1 protocol\\n\");\n\t} else {\n\t\tif (n < 3 || memcmp(buf, \"V3\", 3) != 0)\n\t\t\tdie(\"Cannot handle the protocol %s\", buf);\n\t\t/* OK, let's use v3 protocol */\n\t\twrite(fd, V3_MAGIC, sizeof(V3_MAGIC));\n\n\t\tn = read(fd, buf, BUFSIZ - 1);\n\t\tif (n != 2 || memcmp(buf, \"OK\", 2) != 0) {\n\t\t\tif (n < 0)\n\t\t\t\tn  = 0;\n\t\t\tbuf[n] = 0;\n\t\t\tdie(\"Cannot handle the protocol %s\", buf);\n\t\t}\n\t}\n}\n\nstatic int connect_vsock(char *vhost)\n{\n\tchar *cid;\n\tchar *port;\n\tchar *p;\n\tint sd;\n\n\thost = strdup(vhost);\n\tif (!host)\n\t\tdie(\"alloctating server\");\n\n\tcid = strtok_r(host, \":\", &p);\n\tport = strtok_r(NULL, \"\", &p);\n\n\tif (!port)\n\t\tdie(\"vsocket must have format of 'CID:PORT'\");\n\n\tsd = tcmd_vsock_open(atoi(cid), atoi(port));\n\n\treturn sd;\n}\n\nstatic int connect_ip(char *thost)\n{\n\tstruct addrinfo *result;\n\tint sfd;\n\tchar *server;\n\tchar *port;\n\tchar *p;\n\n\tif (!strchr(thost, ':')) {\n\t\tserver = strdup(\"localhost\");\n\t\tif (!server)\n\t\t\tdie(\"alloctating server\");\n\t\tport = thost;\n\t\thost = server;\n\t} else {\n\t\thost = strdup(thost);\n\t\tif (!host)\n\t\t\tdie(\"alloctating server\");\n\t\tserver = strtok_r(host, \":\", &p);\n\t\tport = strtok_r(NULL, \":\", &p);\n\t}\n\n\tresult = do_getaddrinfo(server, atoi(port), USE_TCP);\n\tif (!result)\n\t\tdie(\"getaddrinfo: %s\", gai_err);\n\n\tsfd = connect_addr(result);\n\n\tfreeaddrinfo(result);\n\n\tif (sfd < 0)\n\t\tdie(\"Can not connect to %s:%s\", server, port);\n\n\treturn sfd;\n}\n\nstatic struct tracecmd_msg_handle *setup_network(struct buffer_instance *instance)\n{\n\tstruct tracecmd_msg_handle *msg_handle = NULL;\n\tenum port_type type = instance->port_type;\n\tchar *thost = strdup(host);\n\tint sfd;\n\n\tif (!thost)\n\t\tdie(\"Failed to allocate host\");\nagain:\n\tswitch (type) {\n\tcase USE_VSOCK:\n\t\tsfd = connect_vsock(thost);\n\t\tbreak;\n\tdefault:\n\t\tsfd = connect_ip(thost);\n\t}\n\n\tif (sfd < 0) {\n\t\tfree(thost);\n\t\ttracecmd_msg_handle_close(msg_handle);\n\t\treturn NULL;\n\t}\n\n\tif (msg_handle) {\n\t\tmsg_handle->fd = sfd;\n\t} else {\n\t\tmsg_handle = tracecmd_msg_handle_alloc(sfd, 0);\n\t\tif (!msg_handle)\n\t\t\tdie(\"Failed to allocate message handle\");\n\n\t\tmsg_handle->cpu_count = local_cpu_count;\n\t\tmsg_handle->version = V3_PROTOCOL;\n\t}\n\n\tswitch (type) {\n\tcase USE_TCP:\n\t\tmsg_handle->flags |= TRACECMD_MSG_FL_USE_TCP;\n\t\tbreak;\n\tcase USE_VSOCK:\n\t\tmsg_handle->flags |= TRACECMD_MSG_FL_USE_VSOCK;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif (msg_handle->version == V3_PROTOCOL) {\n\t\tcheck_protocol_version(msg_handle);\n\t\tif (msg_handle->version == V1_PROTOCOL) {\n\t\t\t/* reconnect to the server for using the v1 protocol */\n\t\t\tclose(sfd);\n\t\t\tmsg_handle->fd = -1;\n\t\t\tfree(host);\n\t\t\thost = NULL;\n\t\t\tgoto again;\n\t\t}\n\t\tcommunicate_with_listener_v3(msg_handle, &instance->client_ports);\n\t}\n\n\tif (msg_handle->version == V1_PROTOCOL)\n\t\tcommunicate_with_listener_v1(msg_handle, instance);\n\n\tfree(thost);\n\treturn msg_handle;\n}\n\nstatic void add_options(struct tracecmd_output *handle, struct common_record_context *ctx);\n\nstatic struct tracecmd_output *create_net_output(struct common_record_context *ctx,\n\t\t\t\t\t\t struct tracecmd_msg_handle *msg_handle)\n{\n\tstruct tracecmd_output *out;\n\n\tout = tracecmd_output_create(NULL);\n\tif (!out)\n\t\treturn NULL;\n\tif (ctx->file_version && tracecmd_output_set_version(out, ctx->file_version))\n\t\tgoto error;\n\tif (tracecmd_output_set_msg(out, msg_handle))\n\t\tgoto error;\n\n\tif (ctx->compression) {\n\t\tif (tracecmd_output_set_compression(out, ctx->compression))\n\t\t\tgoto error;\n\t} else if (ctx->file_version >= FILE_VERSION_COMPRESSION) {\n\t\ttracecmd_output_set_compression(out, \"any\");\n\t}\n\n\tif (tracecmd_output_write_headers(out, listed_events))\n\t\tgoto error;\n\n\treturn out;\nerror:\n\ttracecmd_output_close(out);\n\treturn NULL;\n}\n\nstatic struct tracecmd_msg_handle *\nsetup_connection(struct buffer_instance *instance, struct common_record_context *ctx)\n{\n\tstruct tracecmd_msg_handle *msg_handle = NULL;\n\tstruct tracecmd_output *network_handle = NULL;\n\tint ret;\n\n\tmsg_handle = setup_network(instance);\n\tif (!msg_handle)\n\t\tdie(\"Failed to make connection\");\n\n\t/* Now create the handle through this socket */\n\tif (msg_handle->version == V3_PROTOCOL) {\n\t\tnetwork_handle = create_net_output(ctx, msg_handle);\n\t\tif (!network_handle)\n\t\t\tgoto error;\n\t\ttracecmd_set_quiet(network_handle, quiet);\n\t\tadd_options(network_handle, ctx);\n\t\tret = tracecmd_write_cmdlines(network_handle);\n\t\tif (ret)\n\t\t\tgoto error;\n\t\tret = tracecmd_write_cpus(network_handle, instance->cpu_count);\n\t\tif (ret)\n\t\t\tgoto error;\n\t\tret = tracecmd_write_buffer_info(network_handle);\n\t\tif (ret)\n\t\t\tgoto error;\n\t\tret = tracecmd_write_options(network_handle);\n\t\tif (ret)\n\t\t\tgoto error;\n\t\tret = tracecmd_msg_finish_sending_data(msg_handle);\n\t\tif (ret)\n\t\t\tgoto error;\n\t} else {\n\t\t/*\n\t\t * V3 can handle compression, but V1 can not.\n\t\t * Set the file version back to 6.\n\t\t */\n\t\tctx->file_version = FILE_VERSION_MIN;\n\t\tctx->compression = false;\n\t\tnetwork_handle = tracecmd_output_create_fd(msg_handle->fd);\n\t\tif (!network_handle)\n\t\t\tgoto error;\n\t\tif (tracecmd_output_set_version(network_handle, ctx->file_version))\n\t\t\tgoto error;\n\n\t\tif (tracecmd_output_write_headers(network_handle, listed_events))\n\t\t\tgoto error;\n\t\ttracecmd_set_quiet(network_handle, quiet);\n\t}\n\n\tinstance->network_handle = network_handle;\n\n\t/* OK, we are all set, let'r rip! */\n\treturn msg_handle;\n\nerror:\n\tif (msg_handle)\n\t\ttracecmd_msg_handle_close(msg_handle);\n\tif (network_handle)\n\t\ttracecmd_output_close(network_handle);\n\treturn NULL;\n}\n\nstatic void finish_network(struct tracecmd_msg_handle *msg_handle)\n{\n\tif (msg_handle->version == V3_PROTOCOL)\n\t\ttracecmd_msg_send_close_msg(msg_handle);\n\ttracecmd_msg_handle_close(msg_handle);\n\tfree(host);\n}\n\nstatic int open_guest_fifos(const char *guest, int **fds)\n{\n\tchar path[PATH_MAX];\n\tint i, fd, flags;\n\n\tfor (i = 0; ; i++) {\n\t\tsnprintf(path, sizeof(path), GUEST_FIFO_FMT \".out\", guest, i);\n\n\t\t/* O_NONBLOCK so we don't wait for writers */\n\t\tfd = open(path, O_RDONLY | O_NONBLOCK);\n\t\tif (fd < 0)\n\t\t\tbreak;\n\n\t\t/* Success, now clear O_NONBLOCK */\n\t\tflags = fcntl(fd, F_GETFL);\n\t\tfcntl(fd, F_SETFL, flags & ~O_NONBLOCK);\n\n\t\t*fds = realloc(*fds, i + 1);\n\t\t(*fds)[i] = fd;\n\t}\n\n\treturn i;\n}\n\nstatic bool clock_is_supported(struct tracefs_instance *instance, const char *clock);\n\nstatic int host_tsync(struct common_record_context *ctx,\n\t\t      struct buffer_instance *instance,\n\t\t      unsigned int tsync_port, char *proto)\n{\n\tstruct buffer_instance *iter_instance;\n\tint guest_id = -1;\n\tint fd;\n\n\tif (!proto)\n\t\treturn -1;\n\n\t/* If connecting to a proxy, the clock may still need to be set */\n\tif (strcmp(proto, \"kvm\") == 0 &&\n\t    clock_is_supported(NULL, TSC_CLOCK)) {\n\t\tctx->clock = TSC_CLOCK;\n\t\tfor_all_instances(iter_instance) {\n\t\t\titer_instance->clock = TSC_CLOCK;\n\t\t\tset_clock(ctx, iter_instance);\n\t\t}\n\t}\n\n\tif (is_network(instance)) {\n\t\tfd = connect_port(instance->name, tsync_port,\n\t\t\t\t  instance->port_type);\n\t} else {\n\t\tguest_id = instance->cid;\n\t\tfd = tcmd_vsock_open(instance->cid, tsync_port);\n\t}\n\n\tif (is_proxy(instance)) {\n\t\tinstance->tsync = trace_tsync_as_guest(fd, proto, ctx->clock,\n\t\t\t\t\t\t       guest_id, -1);\n\t} else {\n\t\tinstance->tsync = trace_tsync_as_host(fd, top_instance.trace_id,\n\t\t\t\t\t\t      instance->tsync_loop_interval,\n\t\t\t\t\t\t      guest_id, instance->cpu_count,\n\t\t\t\t\t\t      proto, ctx->clock);\n\t}\n\treturn instance->tsync ? 0 : -1;\n}\n\nstatic void connect_to_agent(struct common_record_context *ctx,\n\t\t\t     struct buffer_instance *instance)\n{\n\tstruct tracecmd_tsync_protos *protos = NULL;\n\tint sd, ret, nr_fifos, nr_cpus, page_size;\n\tstruct tracecmd_msg_handle *msg_handle;\n\tenum tracecmd_time_sync_role role;\n\tchar *tsync_protos_reply = NULL;\n\tunsigned int tsync_port = 0;\n\tunsigned int *ports;\n\tint i, *fds = NULL;\n\tbool use_fifos = false;\n\tint siblings = 0;\n\n\tif (!no_fifos) {\n\t\tnr_fifos = open_guest_fifos(instance->name, &fds);\n\t\tuse_fifos = nr_fifos > 0;\n\t}\n\n\tif (instance->result) {\n\t\trole = TRACECMD_TIME_SYNC_ROLE_CLIENT;\n\t\tsd = connect_addr(instance->result);\n\t\tif (sd < 0)\n\t\t\tdie(\"Failed to connect to host %s:%u\",\n\t\t\t    instance->name, instance->port);\n\t} else {\n\t\t/* If connecting to a proxy, then this is the guest */\n\t\tif (is_proxy(instance))\n\t\t\trole = TRACECMD_TIME_SYNC_ROLE_GUEST;\n\t\telse\n\t\t\trole = TRACECMD_TIME_SYNC_ROLE_HOST;\n\t\tsd = tcmd_vsock_open(instance->cid, instance->port);\n\t\tif (sd < 0)\n\t\t\tdie(\"Failed to connect to vsocket @%u:%u\",\n\t\t\t    instance->cid, instance->port);\n\t}\n\n\tmsg_handle = tracecmd_msg_handle_alloc(sd, 0);\n\tif (!msg_handle)\n\t\tdie(\"Failed to allocate message handle\");\n\n\tif (!instance->clock)\n\t\tinstance->clock = tracefs_get_clock(NULL);\n\n\tif (instance->tsync_loop_interval >= 0)\n\t\ttracecmd_tsync_proto_getall(&protos, instance->clock, role);\n\n\tif (is_proxy(instance))\n\t\tret = tracecmd_msg_send_trace_proxy(msg_handle, instance->argc,\n\t\t\t\t\t\t    instance->argv, use_fifos,\n\t\t\t\t\t\t    top_instance.trace_id, protos,\n\t\t\t\t\t\t    tracecmd_count_cpus(),\n\t\t\t\t\t\t    siblings);\n\telse\n\t\tret = tracecmd_msg_send_trace_req(msg_handle, instance->argc,\n\t\t\t\t\t\t  instance->argv, use_fifos,\n\t\t\t\t\t\t  top_instance.trace_id, protos);\n\tif (ret < 0)\n\t\tdie(\"Failed to send trace %s\",\n\t\t    is_proxy(instance) ? \"proxy\" : \"request\");\n\n\tif (protos) {\n\t\tfree(protos->names);\n\t\tfree(protos);\n\t}\n\tret = tracecmd_msg_recv_trace_resp(msg_handle, &nr_cpus, &page_size,\n\t\t\t\t\t   &ports, &use_fifos,\n\t\t\t\t\t   &instance->trace_id,\n\t\t\t\t\t   &tsync_protos_reply, &tsync_port);\n\tif (ret < 0)\n\t\tdie(\"Failed to receive trace response %d\", ret);\n\tif (tsync_protos_reply && tsync_protos_reply[0]) {\n\t\tif (tcmd_tsync_proto_is_supported(tsync_protos_reply)) {\n\t\t\tprintf(\"Negotiated %s time sync protocol with guest %s\\n\",\n\t\t\t\ttsync_protos_reply,\n\t\t\t\tinstance->name);\n\t\t\tinstance->cpu_count = nr_cpus;\n\t\t\thost_tsync(ctx, instance, tsync_port, tsync_protos_reply);\n\t\t} else\n\t\t\twarning(\"Failed to negotiate timestamps synchronization with the guest\");\n\t}\n\tfree(tsync_protos_reply);\n\n\tif (use_fifos) {\n\t\tif (nr_cpus != nr_fifos) {\n\t\t\twarning(\"number of FIFOs (%d) for guest %s differs \"\n\t\t\t\t\"from number of virtual CPUs (%d)\",\n\t\t\t\tnr_fifos, instance->name, nr_cpus);\n\t\t\tnr_cpus = nr_cpus < nr_fifos ? nr_cpus : nr_fifos;\n\t\t}\n\t\tfree(ports);\n\t\tinstance->fds = fds;\n\t} else {\n\t\tfor (i = 0; i < nr_fifos; i++)\n\t\t\tclose(fds[i]);\n\t\tfree(fds);\n\t\tinstance->client_ports = ports;\n\t}\n\n\tinstance->use_fifos = use_fifos;\n\tinstance->cpu_count = nr_cpus;\n\n\t/* the msg_handle now points to the guest fd */\n\tinstance->msg_handle = msg_handle;\n}\n\nstatic void setup_guest(struct buffer_instance *instance)\n{\n\tstruct tracecmd_msg_handle *msg_handle = instance->msg_handle;\n\tconst char *output_file = instance->output_file;\n\tchar *file;\n\tint fd;\n\n\t/* Create a place to store the guest meta data */\n\tfile = trace_get_guest_file(output_file, instance->name);\n\tif (!file)\n\t\tdie(\"Failed to allocate memory\");\n\n\tfree(instance->output_file);\n\tinstance->output_file = file;\n\n\tfd = open(file, O_CREAT|O_WRONLY|O_TRUNC, 0644);\n\tif (fd < 0)\n\t\tdie(\"Failed to open %s\", file);\n\n\t/* Start reading tracing metadata */\n\tif (tracecmd_msg_read_data(msg_handle, fd))\n\t\tdie(\"Failed receiving metadata\");\n\n\t/*\n\t * If connected to a proxy, then it still needs to send\n\t * the host / guest timings from its POV.\n\t */\n\tif (is_proxy(instance))\n\t\tinstance->proxy_fd = fd;\n\telse\n\t\tclose(fd);\n}\n\nstatic void setup_agent(struct buffer_instance *instance,\n\t\t\tstruct common_record_context *ctx)\n{\n\tstruct tracecmd_output *network_handle;\n\n\tnetwork_handle = create_net_output(ctx, instance->msg_handle);\n\tadd_options(network_handle, ctx);\n\ttracecmd_write_cmdlines(network_handle);\n\ttracecmd_write_cpus(network_handle, instance->cpu_count);\n\ttracecmd_write_buffer_info(network_handle);\n\tif (instance->msg_handle->flags & TRACECMD_MSG_FL_PROXY) {\n\t\ttracecmd_prepare_options(network_handle, 0, SEEK_CUR);\n\t\ttracecmd_msg_flush_data(instance->msg_handle);\n\t} else {\n\t\ttracecmd_write_options(network_handle);\n\t\ttracecmd_write_meta_strings(network_handle);\n\t\ttracecmd_msg_finish_sending_data(instance->msg_handle);\n\t}\n\tinstance->network_handle = network_handle;\n}\n\nstatic void start_threads(enum trace_type type, struct common_record_context *ctx)\n{\n\tstruct buffer_instance *instance;\n\tint total_cpu_count = 0;\n\tint i = 0;\n\tint ret;\n\n\tfor_all_instances(instance) {\n\t\t/* Start the connection now to find out how many CPUs we need */\n\t\tif (is_guest(instance))\n\t\t\tconnect_to_agent(ctx, instance);\n\t\ttotal_cpu_count += instance->cpu_count;\n\t}\n\n\t/* make a thread for every CPU we have */\n\tpids = calloc(total_cpu_count * (buffers + 1), sizeof(*pids));\n\tif (!pids)\n\t\tdie(\"Failed to allocate pids for %d cpus\", total_cpu_count);\n\n\tfor_all_instances(instance) {\n\t\tint *brass = NULL;\n\t\tint x, pid;\n\n\t\t/* May be set by setup_guest() but all others is -1 */\n\t\tinstance->proxy_fd = -1;\n\n\t\tif (is_agent(instance)) {\n\t\t\tsetup_agent(instance, ctx);\n\t\t} else if (is_guest(instance)) {\n\t\t\tsetup_guest(instance);\n\t\t} else if (host) {\n\t\t\tinstance->msg_handle = setup_connection(instance, ctx);\n\t\t\tif (!instance->msg_handle)\n\t\t\t\tdie(\"Failed to make connection\");\n\t\t}\n\n\t\tfor (x = 0; x < instance->cpu_count; x++) {\n\t\t\tif (type & TRACE_TYPE_STREAM) {\n\t\t\t\tbrass = pids[i].brass;\n\t\t\t\tret = pipe(brass);\n\t\t\t\tif (ret < 0)\n\t\t\t\t\tdie(\"pipe\");\n\t\t\t\tpids[i].stream = trace_stream_init(instance, x,\n\t\t\t\t\t\t\t\t   brass[0],\n\t\t\t\t\t\t\t\t   instance->cpu_count,\n\t\t\t\t\t\t\t\t   hooks, handle_init,\n\t\t\t\t\t\t\t\t   ctx->global);\n\t\t\t\tif (!pids[i].stream)\n\t\t\t\t\tdie(\"Creating stream for %d\", i);\n\t\t\t} else\n\t\t\t\tpids[i].brass[0] = -1;\n\t\t\tpids[i].cpu = x;\n\t\t\tpids[i].instance = instance;\n\t\t\t/* Make sure all output is flushed before forking */\n\t\t\tfflush(stdout);\n\t\t\tpid = pids[i++].pid = create_recorder(instance, x, type, brass);\n\t\t\tif (brass)\n\t\t\t\tclose(brass[1]);\n\t\t\tif (pid > 0)\n\t\t\t\tadd_filter_pid(instance, pid, 1);\n\t\t}\n\t}\n\trecorder_threads = i;\n}\n\nstatic void touch_file(const char *file)\n{\n\tint fd;\n\n\tfd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);\n\tif (fd < 0)\n\t\tdie(\"could not create file %s\\n\", file);\n\tclose(fd);\n}\n\nstatic void append_buffer(struct tracecmd_output *handle,\n\t\t\t  struct buffer_instance *instance,\n\t\t\t  char **temp_files)\n{\n\tint cpu_count = instance->cpu_count;\n\tint i;\n\n\t/*\n\t * Since we can record remote and virtual machines in the same file\n\t * as the host, the buffers may no longer have matching number of\n\t * CPU data as the host. For backward compatibility for older\n\t * trace-cmd versions, which will blindly read the number of CPUs\n\t * for each buffer instance as there are for the host, if there are\n\t * fewer CPUs on the remote machine than on the host, an \"empty\"\n\t * CPU is needed for each CPU that the host has that the remote does\n\t * not. If there are more CPUs on the remote, older executables will\n\t * simply ignore them (which is OK, we only need to guarantee that\n\t * old executables don't crash).\n\t */\n\tif (instance->cpu_count < local_cpu_count)\n\t\tcpu_count = local_cpu_count;\n\n\tfor (i = 0; i < cpu_count; i++) {\n\t\ttemp_files[i] = get_temp_file(instance, i);\n\t\tif (i >= instance->cpu_count)\n\t\t\ttouch_file(temp_files[i]);\n\t}\n\n\ttracecmd_append_buffer_cpu_data(handle, tracefs_instance_get_name(instance->tracefs),\n\t\t\t\t\tcpu_count, temp_files);\n\n\tfor (i = 0; i < instance->cpu_count; i++) {\n\t\tif (i >= instance->cpu_count)\n\t\t\tdelete_temp_file(instance, i);\n\t\tput_temp_file(temp_files[i]);\n\t}\n}\n\nstatic void\nadd_pid_maps(struct tracecmd_output *handle, struct buffer_instance *instance)\n{\n\tstruct pid_addr_maps *maps = instance->pid_maps;\n\tstruct trace_seq s;\n\tint i;\n\n\ttrace_seq_init(&s);\n\twhile (maps) {\n\t\tif (!maps->nr_lib_maps) {\n\t\t\tmaps = maps->next;\n\t\t\tcontinue;\n\t\t}\n\t\ttrace_seq_reset(&s);\n\t\ttrace_seq_printf(&s, \"%x %x %s\\n\",\n\t\t\t\t maps->pid, maps->nr_lib_maps, maps->proc_name);\n\t\tfor (i = 0; i < maps->nr_lib_maps; i++)\n\t\t\ttrace_seq_printf(&s, \"%zx %zx %s\\n\",\n\t\t\t\t\tmaps->lib_maps[i].start,\n\t\t\t\t\tmaps->lib_maps[i].end,\n\t\t\t\t\tmaps->lib_maps[i].lib_name);\n\t\ttrace_seq_terminate(&s);\n\t\ttracecmd_add_option(handle, TRACECMD_OPTION_PROCMAPS,\n\t\t\t\t    s.len + 1, s.buffer);\n\t\tmaps = maps->next;\n\t}\n\ttrace_seq_destroy(&s);\n}\n\nstatic void\nadd_trace_id(struct tracecmd_output *handle, struct buffer_instance *instance)\n{\n\ttracecmd_add_option(handle, TRACECMD_OPTION_TRACEID,\n\t\t\t    sizeof(long long), &instance->trace_id);\n}\n\nstatic void\nadd_buffer_stat(struct tracecmd_output *handle, struct buffer_instance *instance)\n{\n\tstruct trace_seq s;\n\tint i;\n\n\ttrace_seq_init(&s);\n\ttrace_seq_printf(&s, \"\\nBuffer: %s\\n\\n\",\n\t\t\ttracefs_instance_get_name(instance->tracefs));\n\ttracecmd_add_option(handle, TRACECMD_OPTION_CPUSTAT,\n\t\t\t    s.len+1, s.buffer);\n\ttrace_seq_destroy(&s);\n\n\tfor (i = 0; i < instance->cpu_count; i++)\n\t\ttracecmd_add_option(handle, TRACECMD_OPTION_CPUSTAT,\n\t\t\t\t    instance->s_save[i].len+1,\n\t\t\t\t    instance->s_save[i].buffer);\n}\n\nstatic void add_option_hooks(struct tracecmd_output *handle)\n{\n\tstruct hook_list *hook;\n\tint len;\n\n\tfor (hook = hooks; hook; hook = hook->next) {\n\t\tlen = strlen(hook->hook);\n\t\ttracecmd_add_option(handle, TRACECMD_OPTION_HOOK,\n\t\t\t\t    len + 1, hook->hook);\n\t}\n}\n\nstatic void add_uname(struct tracecmd_output *handle)\n{\n\tstruct utsname buf;\n\tchar *str;\n\tint len;\n\tint ret;\n\n\tret = uname(&buf);\n\t/* if this fails for some reason, just ignore it */\n\tif (ret < 0)\n\t\treturn;\n\n\tlen = strlen(buf.sysname) + strlen(buf.nodename) +\n\t\tstrlen(buf.release) + strlen(buf.machine) + 4;\n\tstr = malloc(len);\n\tif (!str)\n\t\treturn;\n\tsprintf(str, \"%s %s %s %s\", buf.sysname, buf.nodename, buf.release, buf.machine);\n\ttracecmd_add_option(handle, TRACECMD_OPTION_UNAME, len, str);\n\tfree(str);\n}\n\nstatic void add_version(struct tracecmd_output *handle)\n{\n\tchar *str;\n\tint len;\n\n\tlen = asprintf(&str, \"%s %s\", VERSION_STRING, VERSION_GIT);\n\tif (len < 0)\n\t\treturn;\n\n\ttracecmd_add_option(handle, TRACECMD_OPTION_VERSION, len+1, str);\n\tfree(str);\n}\n\nstatic void print_stat(struct buffer_instance *instance)\n{\n\tint cpu;\n\n\tif (quiet)\n\t\treturn;\n\n\tif (!is_top_instance(instance))\n\t\tprintf(\"\\nBuffer: %s\\n\\n\",\n\t\t\ttracefs_instance_get_name(instance->tracefs));\n\n\tfor (cpu = 0; cpu < instance->cpu_count; cpu++)\n\t\ttrace_seq_do_printf(&instance->s_print[cpu]);\n}\n\nstatic char *get_trace_clock(bool selected)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance))\n\t\t\tcontinue;\n\t\tbreak;\n\t}\n\n\tif (selected)\n\t\treturn tracefs_get_clock(instance ? instance->tracefs : NULL);\n\telse\n\t\treturn tracefs_instance_file_read(instance ? instance->tracefs : NULL,\n\t\t\t\t\t\t  \"trace_clock\", NULL);\n}\n\nenum {\n\tDATA_FL_NONE\t\t= 0,\n\tDATA_FL_DATE\t\t= 1,\n\tDATA_FL_OFFSET\t\t= 2,\n\tDATA_FL_GUEST\t\t= 4,\n\tDATA_FL_PROXY\t\t= 8,\n};\n\nstatic void add_options(struct tracecmd_output *handle, struct common_record_context *ctx)\n{\n\tint type = 0;\n\tchar *clocks;\n\n\tif (ctx->date2ts) {\n\t\tif (ctx->data_flags & DATA_FL_DATE)\n\t\t\ttype = TRACECMD_OPTION_DATE;\n\t\telse if (ctx->data_flags & DATA_FL_OFFSET)\n\t\t\ttype = TRACECMD_OPTION_OFFSET;\n\t}\n\n\tif (type)\n\t\ttracecmd_add_option(handle, type, strlen(ctx->date2ts)+1, ctx->date2ts);\n\n\tclocks = get_trace_clock(false);\n\ttracecmd_add_option(handle, TRACECMD_OPTION_TRACECLOCK,\n\t\t\t    clocks ? strlen(clocks)+1 : 0, clocks);\n\tadd_option_hooks(handle);\n\tadd_uname(handle);\n\tadd_version(handle);\n\tif (!no_top_instance())\n\t\tadd_trace_id(handle, &top_instance);\n\tfree(clocks);\n}\n\nstatic void write_guest_file(struct buffer_instance *instance)\n{\n\tstruct tracecmd_output *handle;\n\tint cpu_count = instance->cpu_count;\n\tchar *file;\n\tchar **temp_files;\n\tint i, fd;\n\n\tfile = instance->output_file;\n\tfd = open(file, O_RDWR);\n\tif (fd < 0)\n\t\tdie(\"error opening %s\", file);\n\n\thandle = tracecmd_get_output_handle_fd(fd);\n\tif (!handle)\n\t\tdie(\"error writing to %s\", file);\n\tif (instance->flags & BUFFER_FL_TSC2NSEC)\n\t\ttracecmd_set_out_clock(handle, TSCNSEC_CLOCK);\n\ttemp_files = malloc(sizeof(*temp_files) * cpu_count);\n\tif (!temp_files)\n\t\tdie(\"failed to allocate temp_files for %d cpus\",\n\t\t    cpu_count);\n\n\tfor (i = 0; i < cpu_count; i++) {\n\t\ttemp_files[i] = get_temp_file(instance, i);\n\t\tif (!temp_files[i])\n\t\t\tdie(\"failed to allocate memory\");\n\t}\n\n\tif (tracecmd_write_cpu_data(handle, cpu_count, temp_files, NULL) < 0)\n\t\tdie(\"failed to write CPU data\");\n\ttracecmd_output_close(handle);\n\n\tfor (i = 0; i < cpu_count; i++)\n\t\tput_temp_file(temp_files[i]);\n\tfree(temp_files);\n}\n\nstatic struct tracecmd_output *create_output(struct common_record_context *ctx)\n{\n\tstruct tracecmd_output *out;\n\n\tif (!ctx->output)\n\t\treturn NULL;\n\n\tout = tracecmd_output_create(ctx->output);\n\tif (!out)\n\t\tgoto error;\n\tif (ctx->file_version && tracecmd_output_set_version(out, ctx->file_version))\n\t\tgoto error;\n\n\tif (ctx->compression) {\n\t\tif (tracecmd_output_set_compression(out, ctx->compression))\n\t\t\tgoto error;\n\t} else if (ctx->file_version >= FILE_VERSION_COMPRESSION) {\n\t\ttracecmd_output_set_compression(out, \"any\");\n\t}\n\n\tif (tracecmd_output_write_headers(out, listed_events))\n\t\tgoto error;\n\n\treturn out;\nerror:\n\tif (out)\n\t\ttracecmd_output_close(out);\n\tunlink(ctx->output);\n\treturn NULL;\n}\n\nstatic void check_need_btf(bool *need_btf, struct tracefs_instance *instance)\n{\n\tbool func_graph_trace;\n\tbool func_trace;\n\tchar *current;\n\n\t/* Nothing to do if it's already set */\n\tif (*need_btf)\n\t\treturn;\n\n\tcurrent = tracefs_instance_file_read(instance, \"current_tracer\", NULL);\n\tif (!current)\n\t\treturn;\n\n\tfunc_trace = strcmp(current, \"function\\n\") == 0;\n\tfunc_graph_trace = strcmp(current, \"function_graph\\n\") == 0;\n\n\tfree(current);\n\tif (!func_trace && !func_graph_trace)\n\t\treturn;\n\n\tif (func_trace)\n\t\tcurrent = tracefs_instance_file_read(instance, \"options/func-args\", NULL);\n\telse\n\t\tcurrent = tracefs_instance_file_read(instance, \"options/funcgraph-args\", NULL);\n\tif (!current)\n\t\treturn;\n\n\t*need_btf = strncmp(current, \"1\", 1) == 0;\n\n\tfree(current);\n}\n\n#define IS_EXTRACT(ctx) ((ctx)->curr_cmd == CMD_extract)\n#define IS_START(ctx) ((ctx)->curr_cmd == CMD_start)\n#define IS_CMDSET(ctx) ((ctx)->curr_cmd == CMD_set)\n#define IS_STREAM(ctx) ((ctx)->curr_cmd == CMD_stream)\n#define IS_PROFILE(ctx) ((ctx)->curr_cmd == CMD_profile)\n#define IS_RECORD(ctx) ((ctx)->curr_cmd == CMD_record)\n#define IS_RECORD_AGENT(ctx) ((ctx)->curr_cmd == CMD_record_agent)\n\nstatic void record_data(struct common_record_context *ctx)\n{\n\tstruct tracecmd_output *handle;\n\tstruct buffer_instance *instance;\n\tbool have_proxy = false;\n\tbool local = false;\n\tbool need_btf = IS_EXTRACT(ctx);\n\tint max_cpu_count = local_cpu_count;\n\tchar **temp_files;\n\tint i;\n\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance)) {\n\t\t\twrite_guest_file(instance);\n\t\t\tif (is_proxy(instance))\n\t\t\t\thave_proxy = true;\n\t\t} else if (host && instance->msg_handle)\n\t\t\tfinish_network(instance->msg_handle);\n\t\telse\n\t\t\tlocal = true;\n\t}\n\n\tif (!local)\n\t\treturn;\n\n\tif (latency) {\n\t\thandle = tracecmd_create_file_latency(ctx->output, local_cpu_count,\n\t\t\t\t\t\t      ctx->file_version, ctx->compression);\n\t\ttracecmd_set_quiet(handle, quiet);\n\t} else {\n\t\tif (!local_cpu_count)\n\t\t\treturn;\n\n\t\t/* Allocate enough temp files to handle each instance */\n\t\tfor_all_instances(instance) {\n\t\t\tif (instance->msg_handle)\n\t\t\t\tcontinue;\n\t\t\tif (instance->cpu_count > max_cpu_count)\n\t\t\t\tmax_cpu_count = instance->cpu_count;\n\t\t}\n\n\t\ttemp_files = malloc(sizeof(*temp_files) * max_cpu_count);\n\t\tif (!temp_files)\n\t\t\tdie(\"Failed to allocate temp_files for %d cpus\",\n\t\t\t    local_cpu_count);\n\n\t\tfor (i = 0; i < max_cpu_count; i++)\n\t\t\ttemp_files[i] = get_temp_file(&top_instance, i);\n\n\t\t/*\n\t\t * If top_instance was not used, we still need to create\n\t\t * empty trace.dat files for it.\n\t\t */\n\t\tif (no_top_instance() || top_instance.msg_handle) {\n\t\t\tfor (i = 0; i < local_cpu_count; i++)\n\t\t\t\ttouch_file(temp_files[i]);\n\t\t}\n\n\t\thandle = create_output(ctx);\n\t\tif (!handle)\n\t\t\tdie(\"Error creating output file\");\n\t\ttracecmd_set_quiet(handle, quiet);\n\n\t\tadd_options(handle, ctx);\n\n\t\t/*\n\t\t * If we connected to a proxy, then it will now send us\n\t\t * the tsync data for our file.\n\t\t */\n\t\tif (have_proxy) {\n\t\t\tfor_all_instances(instance) {\n\t\t\t\tif (!is_proxy(instance))\n\t\t\t\t\tcontinue;\n\t\t\t\t/* Tell proxy we are ready for the rest */\n\t\t\t\ttracecmd_msg_cont(instance->msg_handle);\n\t\t\t\ttracecmd_msg_read_options(instance->msg_handle, handle);\n\t\t\t\ttracecmd_msg_wait_close_resp(instance->msg_handle);\n\t\t\t\ttracecmd_msg_handle_close(instance->msg_handle);\n\t\t\t}\n\t\t}\n\n\t\t/* Only record the top instance under TRACECMD_OPTION_CPUSTAT*/\n\t\tif (!no_top_instance() && !top_instance.msg_handle) {\n\t\t\tstruct trace_seq *s = top_instance.s_save;\n\n\t\t\tfor (i = 0; i < local_cpu_count; i++)\n\t\t\t\ttracecmd_add_option(handle, TRACECMD_OPTION_CPUSTAT,\n\t\t\t\t\t\t    s[i].len+1, s[i].buffer);\n\t\t}\n\n\t\tif (buffers) {\n\t\t\ti = 0;\n\t\t\tfor_each_instance(instance) {\n\t\t\t\tint cpus = instance->cpu_count != local_cpu_count ?\n\t\t\t\t\tinstance->cpu_count : 0;\n\n\t\t\t\tif (instance->msg_handle)\n\t\t\t\t\tcontinue;\n\t\t\t\ttracecmd_add_buffer_info(handle,\n\t\t\t\t\t\t\ttracefs_instance_get_name(instance->tracefs),\n\t\t\t\t\t\t\tcpus);\n\t\t\t\tadd_buffer_stat(handle, instance);\n\t\t\t\tcheck_need_btf(&need_btf, instance->tracefs);\n\t\t\t\tif (instance->last_boot_info && instance->name) {\n\t\t\t\t\tchar *buf;\n\t\t\t\t\tint len;\n\n\t\t\t\t\tif (asprintf(&buf, \"%s:%s\", instance->name,\n\t\t\t\t\t\t     instance->last_boot_info) < 0) {\n\t\t\t\t\t\twarning(\"Failed to add last_boot_info\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tlen = strlen(instance->name);\n\t\t\t\t\tlen += strlen(instance->last_boot_info) + 2;\n\n\t\t\t\t\ttracecmd_add_option(handle, TRACECMD_OPTION_LAST_BOOT_INFO,\n\t\t\t\t\t\t\t    len, buf);\n\t\t\t\t\tfree(buf);\n\n\t\t\t\t\t/* Also add modules */\n\t\t\t\t\ttracecmd_append_modules_file(handle);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!no_top_instance() && !top_instance.msg_handle) {\n\t\t\tprint_stat(&top_instance);\n\t\t\tcheck_need_btf(&need_btf, top_instance.tracefs);\n\t\t}\n\n\t\tif (need_btf)\n\t\t\ttracecmd_append_btf_file(handle);\n\n\t\tfor_all_instances(instance) {\n\t\t\tadd_pid_maps(handle, instance);\n\t\t}\n\n\t\tfor_all_instances(instance) {\n\t\t\tif (is_guest(instance))\n\t\t\t\ttrace_add_guest_info(handle, instance);\n\t\t}\n\n\t\tif (ctx->tsc2nsec.mult) {\n\t\t\tadd_tsc2nsec(handle, &ctx->tsc2nsec);\n\t\t\ttracecmd_set_out_clock(handle, TSCNSEC_CLOCK);\n\t\t}\n\t\tif (tracecmd_write_cmdlines(handle))\n\t\t\tdie(\"Writing cmdlines\");\n\n\t\ttracecmd_append_cpu_data(handle, local_cpu_count, temp_files);\n\n\t\tfor (i = 0; i < max_cpu_count; i++)\n\t\t\tput_temp_file(temp_files[i]);\n\n\t\tif (buffers) {\n\t\t\ti = 0;\n\t\t\tfor_each_instance(instance) {\n\t\t\t\tif (instance->msg_handle)\n\t\t\t\t\tcontinue;\n\t\t\t\tprint_stat(instance);\n\t\t\t\tappend_buffer(handle, instance, temp_files);\n\t\t\t}\n\t\t}\n\n\t\tfree(temp_files);\n\t}\n\tif (!handle)\n\t\tdie(\"could not write to file\");\n\ttracecmd_output_close(handle);\n}\n\nenum filter_type {\n\tFUNC_FILTER,\n\tFUNC_NOTRACE,\n};\n\nstatic int filter_command(struct tracefs_instance *instance, const char *cmd)\n{\n\treturn tracefs_instance_file_append(instance, \"set_ftrace_filter\", cmd);\n}\n\nstatic int write_func_filter(enum filter_type type, struct buffer_instance *instance,\n\t\t\t     struct func_list **list)\n{\n\tstruct func_list *item, *cmds = NULL;\n\tconst char *file;\n\tint ret = -1;\n\tint (*filter_function)(struct tracefs_instance *instance, const char *filter,\n\t\t\t       const char *module, unsigned int flags);\n\n\tif (!*list)\n\t\treturn 0;\n\n\tswitch (type) {\n\tcase FUNC_FILTER:\n\t\tfilter_function = tracefs_function_filter;\n\t\tfile = \"set_ftrace_filter\";\n\t\tbreak;\n\tcase FUNC_NOTRACE:\n\t\tfilter_function = tracefs_function_notrace;\n\t\tfile = \"set_ftrace_notrace\";\n\t\tbreak;\n\t}\n\n\tret = filter_function(instance->tracefs, NULL, NULL,\n\t\t\t      TRACEFS_FL_RESET | TRACEFS_FL_CONTINUE);\n\tif (ret < 0)\n\t\treturn ret;\n\n\twhile (*list) {\n\t\titem = *list;\n\t\t*list = item->next;\n\t\t/* Do commands separately at the end */\n\t\tif (type == FUNC_FILTER && strstr(item->func, \":\")) {\n\t\t\titem->next = cmds;\n\t\t\tcmds = item;\n\t\t\tcontinue;\n\t\t}\n\t\tret = filter_function(instance->tracefs, item->func, item->mod,\n\t\t\t\t      TRACEFS_FL_CONTINUE);\n\t\tif (ret < 0)\n\t\t\tgoto failed;\n\t\tfree(item);\n\t}\n\tret = filter_function(instance->tracefs, NULL, NULL, 0);\n\n\t/* Now add any commands */\n\twhile (cmds) {\n\t\titem = cmds;\n\t\tcmds = item->next;\n\t\tret = filter_command(instance->tracefs, item->func);\n\t\tif (ret < 0)\n\t\t\tgoto failed;\n\t\tfree(item);\n\t}\n\treturn ret;\n failed:\n\tdie(\"Failed to write %s to %s.\\n\"\n\t    \"Perhaps this function is not available for tracing.\\n\"\n\t    \"run 'trace-cmd list -f %s' to see if it is.\",\n\t    item->func, file, item->func);\n\treturn ret;\n}\n\nstatic int write_func_file(struct buffer_instance *instance,\n\t\t\t    const char *file, struct func_list **list)\n{\n\tstruct func_list *item;\n\tconst char *prefix = \":mod:\";\n\tchar *path;\n\tint fd;\n\tint ret = -1;\n\n\tif (!*list)\n\t\treturn 0;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, file);\n\n\tfd = open(path, O_WRONLY | O_TRUNC);\n\tif (fd < 0)\n\t\tgoto free;\n\n\twhile (*list) {\n\t\titem = *list;\n\t\t*list = item->next;\n\t\tret = write(fd, item->func, strlen(item->func));\n\t\tif (ret < 0)\n\t\t\tgoto failed;\n\t\tif (item->mod) {\n\t\t\tret = write(fd, prefix, strlen(prefix));\n\t\t\tif (ret < 0)\n\t\t\t\tgoto failed;\n\t\t\tret = write(fd, item->mod, strlen(item->mod));\n\t\t\tif (ret < 0)\n\t\t\t\tgoto failed;\n\t\t}\n\t\tret = write(fd, \" \", 1);\n\t\tif (ret < 0)\n\t\t\tgoto failed;\n\t\tfree(item);\n\t}\n\tclose(fd);\n\tret = 0;\n free:\n\ttracefs_put_tracing_file(path);\n\treturn ret;\n failed:\n\tdie(\"Failed to write %s to %s.\\n\"\n\t    \"Perhaps this function is not available for tracing.\\n\"\n\t    \"run 'trace-cmd list -f %s' to see if it is.\",\n\t    item->func, file, item->func);\n\treturn ret;\n}\n\nstatic int functions_filtered(struct buffer_instance *instance)\n{\n\tchar buf[1] = { '#' };\n\tchar *path;\n\tint fd;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"set_ftrace_filter\");\n\tfd = open(path, O_RDONLY);\n\ttracefs_put_tracing_file(path);\n\tif (fd < 0) {\n\t\tif (is_top_instance(instance))\n\t\t\twarning(\"Can not set set_ftrace_filter\");\n\t\telse\n\t\t\twarning(\"Can not set set_ftrace_filter for %s\",\n\t\t\t\ttracefs_instance_get_name(instance->tracefs));\n\t\treturn 0;\n\t}\n\n\t/*\n\t * If functions are not filtered, than the first character\n\t * will be '#'. Make sure it is not an '#' and also not space.\n\t */\n\tread(fd, buf, 1);\n\tclose(fd);\n\n\tif (buf[0] == '#' || isspace(buf[0]))\n\t\treturn 0;\n\treturn 1;\n}\n\nstatic void set_funcs(struct buffer_instance *instance)\n{\n\tint set_notrace = 0;\n\tint ret;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tret = write_func_filter(FUNC_FILTER, instance, &instance->filter_funcs);\n\tif (ret < 0)\n\t\tdie(\"set_ftrace_filter does not exist. Can not filter functions\");\n\n\t/* graph tracing currently only works for top instance */\n\tif (is_top_instance(instance)) {\n\t\tret = write_func_file(instance, \"set_graph_function\", &graph_funcs);\n\t\tif (ret < 0)\n\t\t\tdie(\"set_graph_function does not exist.\");\n\t\tif (instance->plugin && strcmp(instance->plugin, \"function_graph\") == 0) {\n\t\t\tret = write_func_file(instance, \"set_graph_notrace\",\n\t\t\t\t\t      &instance->notrace_funcs);\n\t\t\tif (!ret)\n\t\t\t\tset_notrace = 1;\n\t\t}\n\t\tif (!set_notrace) {\n\t\t\tret = write_func_filter(FUNC_NOTRACE, instance,\n\t\t\t\t\t      &instance->notrace_funcs);\n\t\t\tif (ret < 0)\n\t\t\t\tdie(\"set_ftrace_notrace does not exist. Can not filter functions\");\n\t\t}\n\t} else\n\t\twrite_func_filter(FUNC_NOTRACE, instance, &instance->notrace_funcs);\n\n\t/* make sure we are filtering functions */\n\tif (func_stack && is_top_instance(instance)) {\n\t\tif (!functions_filtered(instance))\n\t\t\tdie(\"Function stack trace set, but functions not filtered\");\n\t\tsave_option(instance, FUNC_STACK_TRACE);\n\t}\n\tclear_function_filters = 1;\n}\n\nstatic void add_func(struct func_list **list, const char *mod, const char *func)\n{\n\tstruct func_list *item;\n\n\titem = malloc(sizeof(*item));\n\tif (!item)\n\t\tdie(\"Failed to allocate function descriptor\");\n\titem->func = func;\n\titem->mod = mod;\n\titem->next = *list;\n\t*list = item;\n}\n\nstatic int find_ts(struct tep_event *event, struct tep_record *record,\n\t\t   int cpu, void *context)\n{\n\tunsigned long long *ts = (unsigned long long *)context;\n\tstruct tep_format_field *field;\n\n\tif (!ts)\n\t\treturn -1;\n\n\tfield = tep_find_field(event, \"buf\");\n\tif (field && strcmp(STAMP\"\\n\", record->data + field->offset) == 0) {\n\t\t*ts = record->ts;\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\nstatic unsigned long long find_time_stamp(struct tep_handle *tep,\n\t\t\t\t\t  struct tracefs_instance *instance)\n{\n\tunsigned long long ts = 0;\n\n\tif (!tracefs_iterate_raw_events(tep, instance, NULL, 0, find_ts, &ts))\n\t\treturn ts;\n\n\treturn 0;\n}\n\n\nstatic char *read_top_file(char *file, int *psize)\n{\n\treturn tracefs_instance_file_read(top_instance.tracefs, file, psize);\n}\n\nstatic struct tep_handle *get_ftrace_tep(void)\n{\n\tconst char *systems[] = {\"ftrace\", NULL};\n\tstruct tep_handle *tep;\n\tchar *buf;\n\tint size;\n\tint ret;\n\n\ttep = tracefs_local_events_system(NULL, systems);\n\tif (!tep)\n\t\treturn NULL;\n\ttep_set_file_bigendian(tep, tracecmd_host_bigendian());\n\tbuf = read_top_file(\"events/header_page\", &size);\n\tif (!buf)\n\t\tgoto error;\n\tret = tep_parse_header_page(tep, buf, size, sizeof(unsigned long));\n\tfree(buf);\n\tif (ret < 0)\n\t\tgoto error;\n\n\treturn tep;\n\nerror:\n\ttep_free(tep);\n\treturn NULL;\n}\n\n/*\n * Try to write the date into the ftrace buffer and then\n * read it back, mapping the timestamp to the date.\n */\nstatic char *get_date_to_ts(void)\n{\n\tstruct tep_handle *tep;\n\tunsigned long long min = -1ULL;\n\tunsigned long long diff;\n\tunsigned long long stamp;\n\tunsigned long long min_stamp;\n\tunsigned long long min_ts;\n\tunsigned long long ts;\n\tstruct timespec start;\n\tstruct timespec end;\n\tchar *date2ts = NULL;\n\tint tfd;\n\tint i;\n\n\t/* Set up a tep to read the raw format */\n\ttep = get_ftrace_tep();\n\tif (!tep) {\n\t\twarning(\"failed to alloc tep, --date ignored\");\n\t\treturn NULL;\n\t}\n\ttfd = tracefs_instance_file_open(NULL, \"trace_marker\", O_WRONLY);\n\tif (tfd < 0) {\n\t\twarning(\"Can not open 'trace_marker', --date ignored\");\n\t\tgoto out_pevent;\n\t}\n\n\tfor (i = 0; i < date2ts_tries; i++) {\n\t\ttracecmd_disable_tracing();\n\t\tclear_trace_instances();\n\t\ttracecmd_enable_tracing();\n\n\t\tclock_gettime(CLOCK_REALTIME, &start);\n\t\twrite(tfd, STAMP, 5);\n\t\tclock_gettime(CLOCK_REALTIME, &end);\n\n\t\ttracecmd_disable_tracing();\n\t\tts = find_time_stamp(tep, NULL);\n\t\tif (!ts)\n\t\t\tcontinue;\n\n\t\tdiff = (unsigned long long)end.tv_sec * 1000000000LL;\n\t\tdiff += (unsigned long long)end.tv_nsec;\n\t\tstamp = diff;\n\t\tdiff -= (unsigned long long)start.tv_sec * 1000000000LL;\n\t\tdiff -= (unsigned long long)start.tv_nsec;\n\n\t\tif (diff < min) {\n\t\t\tmin_ts = ts;\n\t\t\tmin_stamp = stamp - diff / 2;\n\t\t\tmin = diff;\n\t\t}\n\t}\n\n\tclose(tfd);\n\n\tif (min == -1ULL) {\n\t\twarning(\"Failed to make date offset, --date ignored\");\n\t\tgoto out_pevent;\n\t}\n\n\t/* 16 hex chars + 0x + \\0 */\n\tdate2ts = malloc(19);\n\tif (!date2ts)\n\t\tgoto out_pevent;\n\n\t/*\n\t * The difference between the timestamp and the gtod is\n\t * stored as an ASCII string in hex.\n\t */\n\tdiff = min_stamp - min_ts;\n\tsnprintf(date2ts, 19, \"0x%llx\", diff/1000);\n out_pevent:\n\ttep_free(tep);\n\n\treturn date2ts;\n}\n\nstatic void set_buffer_size_instance(struct buffer_instance *instance)\n{\n\tint buffer_size = instance->buffer_size;\n\tint ret;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tif (!buffer_size)\n\t\treturn;\n\n\tif (buffer_size < 0)\n\t\tdie(\"buffer size must be positive\");\n\n\tinstance->old_buffer_size = tracefs_instance_get_buffer_size(instance->tracefs, 0);\n\tret = tracefs_instance_set_buffer_size(instance->tracefs, buffer_size, -1);\n\tif (ret < 0)\n\t\twarning(\"Can't set buffer size\");\n}\n\nstatic void set_subbuf_size_instance(struct buffer_instance *instance)\n{\n\tint subbuf_size = instance->subbuf_size;\n\tint ret;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tif (!subbuf_size)\n\t\treturn;\n\n\tif (subbuf_size < 0)\n\t\tdie(\"sub-buffer size must be positive\");\n\n\tinstance->old_subbuf_size = tracefs_instance_get_subbuf_size(instance->tracefs);\n\tret = tracefs_instance_set_subbuf_size(instance->tracefs, subbuf_size);\n\tif (ret < 0)\n\t\twarning(\"Can't set sub-buffer size\");\n}\n\nvoid set_buffer_size(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance) {\n\t\tset_buffer_size_instance(instance);\n\t\tset_subbuf_size_instance(instance);\n\t}\n}\n\nstatic int\nprocess_event_trigger(char *path, struct event_iter *iter)\n{\n\tconst char *system = iter->system_dent->d_name;\n\tconst char *event = iter->event_dent->d_name;\n\tstruct stat st;\n\tchar *trigger = NULL;\n\tchar *file;\n\tint ret;\n\n\tpath = append_file(path, system);\n\tfile = append_file(path, event);\n\tfree(path);\n\n\tret = stat(file, &st);\n\tif (ret < 0 || !S_ISDIR(st.st_mode))\n\t\tgoto out;\n\n\ttrigger = append_file(file, \"trigger\");\n\n\tret = stat(trigger, &st);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tret = clear_trigger(trigger);\n out:\n\tfree(trigger);\n\tfree(file);\n\treturn ret;\n}\n\nstatic void clear_instance_triggers(struct buffer_instance *instance)\n{\n\tenum event_iter_type type;\n\tstruct event_iter *iter;\n\tchar *system;\n\tchar *path;\n\tint retry = 0;\n\tint ret;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"events\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\titer = trace_event_iter_alloc(path);\n\n\tsystem = NULL;\n\twhile ((type = trace_event_iter_next(iter, path, system))) {\n\n\t\tif (type == EVENT_ITER_SYSTEM) {\n\t\t\tsystem = iter->system_dent->d_name;\n\t\t\tcontinue;\n\t\t}\n\n\t\tret = process_event_trigger(path, iter);\n\t\tif (ret > 0)\n\t\t\tretry++;\n\t}\n\n\ttrace_event_iter_free(iter);\n\n\tif (retry) {\n\t\tint i;\n\n\t\t/* Order matters for some triggers */\n\t\tfor (i = 0; i < retry; i++) {\n\t\t\tint tries = 0;\n\n\t\t\titer = trace_event_iter_alloc(path);\n\t\t\tsystem = NULL;\n\t\t\twhile ((type = trace_event_iter_next(iter, path, system))) {\n\n\t\t\t\tif (type == EVENT_ITER_SYSTEM) {\n\t\t\t\t\tsystem = iter->system_dent->d_name;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tret = process_event_trigger(path, iter);\n\t\t\t\tif (ret > 0)\n\t\t\t\t\ttries++;\n\t\t\t}\n\t\t\ttrace_event_iter_free(iter);\n\t\t\tif (!tries)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void\nprocess_event_filter(char *path, struct event_iter *iter, enum event_process *processed)\n{\n\tconst char *system = iter->system_dent->d_name;\n\tconst char *event = iter->event_dent->d_name;\n\tstruct stat st;\n\tchar *filter = NULL;\n\tchar *file;\n\tint ret;\n\n\tpath = append_file(path, system);\n\tfile = append_file(path, event);\n\tfree(path);\n\n\tret = stat(file, &st);\n\tif (ret < 0 || !S_ISDIR(st.st_mode))\n\t\tgoto out;\n\n\tfilter = append_file(file, \"filter\");\n\n\tret = stat(filter, &st);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tclear_filter(filter);\n out:\n\tfree(filter);\n\tfree(file);\n}\n\nstatic void clear_instance_filters(struct buffer_instance *instance)\n{\n\tstruct event_iter *iter;\n\tchar *path;\n\tchar *system;\n\tenum event_iter_type type;\n\tenum event_process processed = PROCESSED_NONE;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"events\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\titer = trace_event_iter_alloc(path);\n\n\tprocessed = PROCESSED_NONE;\n\tsystem = NULL;\n\twhile ((type = trace_event_iter_next(iter, path, system))) {\n\n\t\tif (type == EVENT_ITER_SYSTEM) {\n\t\t\tsystem = iter->system_dent->d_name;\n\t\t\tcontinue;\n\t\t}\n\n\t\tprocess_event_filter(path, iter, &processed);\n\t}\n\n\ttrace_event_iter_free(iter);\n\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void clear_filters(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tclear_instance_filters(instance);\n}\n\nstatic void reset_clock(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\ttracefs_instance_file_write(instance->tracefs,\n\t\t\t\t\t    \"trace_clock\", \"local\");\n}\n\nstatic void reset_cpu_mask(void)\n{\n\tstruct buffer_instance *instance;\n\tint cpus = tracecmd_count_cpus();\n\tint fullwords = (cpus - 1) / 32;\n\tint bits = (cpus - 1) % 32 + 1;\n\tint len = (fullwords + 1) * 9;\n\tchar buf[len + 1];\n\n\tbuf[0] = '\\0';\n\n\tsprintf(buf, \"%x\", (unsigned int)((1ULL << bits) - 1));\n\twhile (fullwords-- > 0)\n\t\tstrcat(buf, \",ffffffff\");\n\n\tfor_all_instances(instance)\n\t\ttracefs_instance_file_write(instance->tracefs,\n\t\t\t\t\t    \"tracing_cpumask\", buf);\n}\n\nstatic void reset_event_pid(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tadd_event_pid(instance, \"\");\n}\n\nstatic void clear_triggers(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tclear_instance_triggers(instance);\n}\n\nstatic void clear_instance_error_log(struct buffer_instance *instance)\n{\n\tchar *file;\n\n\tif (!tracefs_file_exists(instance->tracefs, \"error_log\"))\n\t\treturn;\n\n\tfile = tracefs_instance_get_file(instance->tracefs, \"error_log\");\n\tif (!file)\n\t\treturn;\n\twrite_file(file, \" \");\n\ttracefs_put_tracing_file(file);\n}\n\nstatic void clear_error_log(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tclear_instance_error_log(instance);\n}\n\nstatic void clear_all_dynamic_events(unsigned int excluded_types)\n{\n\t/*\n\t * Clear event probes first (if they are not excluded), as they may be\n\t * attached to other dynamic event.\n\t */\n\tif (!(excluded_types & TRACEFS_DYNEVENT_EPROBE))\n\t\ttracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_EPROBE, true);\n\ttracefs_dynevent_destroy_all(~excluded_types & TRACEFS_DYNEVENT_ALL, true);\n}\n\nstatic void clear_func_filters(void)\n{\n\tstruct buffer_instance *instance;\n\tchar *path;\n\tint i;\n\tconst char * const files[] = { \"set_ftrace_filter\",\n\t\t\t\t      \"set_ftrace_notrace\",\n\t\t\t\t      \"set_graph_function\",\n\t\t\t\t      \"set_graph_notrace\",\n\t\t\t\t      NULL };\n\n\tfor_all_instances(instance) {\n\t\tfor (i = 0; files[i]; i++) {\n\t\t\tpath = tracefs_instance_get_file(instance->tracefs, files[i]);\n\t\t\tclear_func_filter(path);\n\t\t\ttracefs_put_tracing_file(path);\n\t\t}\n\t}\n}\n\nstatic void make_instances(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_each_instance(instance) {\n\t\tif (is_guest(instance))\n\t\t\tcontinue;\n\t\tif (instance->name && !instance->tracefs) {\n\t\t\tinstance->tracefs = tracefs_instance_create(instance->name);\n\t\t\t/* Don't delete instances that already exist */\n\t\t\tif (instance->tracefs && !tracefs_instance_is_new(instance->tracefs))\n\t\t\t\tinstance->flags |= BUFFER_FL_KEEP;\n\t\t}\n\t}\n}\n\nvoid tracecmd_remove_instances(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_each_instance(instance) {\n\t\t/* Only delete what we created */\n\t\tif (is_guest(instance) || (instance->flags & BUFFER_FL_KEEP))\n\t\t\tcontinue;\n\t\tif (instance->tracing_on_fd > 0) {\n\t\t\tclose(instance->tracing_on_fd);\n\t\t\tinstance->tracing_on_fd = 0;\n\t\t}\n\t\ttracefs_instance_destroy(instance->tracefs);\n\t}\n}\n\nstatic void check_plugin(const char *plugin)\n{\n\tchar *buf;\n\tchar *str;\n\tchar *tok;\n\n\t/*\n\t * nop is special. We may want to just trace\n\t * trace_printks, that are in the kernel.\n\t */\n\tif (strcmp(plugin, \"nop\") == 0)\n\t\treturn;\n\n\tbuf = read_top_file(\"available_tracers\", NULL);\n\tif (!buf)\n\t\tdie(\"No plugins available\");\n\n\tstr = buf;\n\twhile ((tok = strtok(str, \" \"))) {\n\t\tstr = NULL;\n\t\tif (strcmp(tok, plugin) == 0)\n\t\t\tgoto out;\n\t}\n\tdie (\"Plugin '%s' does not exist\", plugin);\n out:\n\tif (!quiet)\n\t\tfprintf(stderr, \"  plugin '%s'\\n\", plugin);\n\tfree(buf);\n}\n\nstatic void check_function_plugin(void)\n{\n\tconst char *plugin;\n\n\t/* We only care about the top_instance */\n\tif (no_top_instance())\n\t\treturn;\n\n\tplugin = top_instance.plugin;\n\tif (!plugin)\n\t\treturn;\n\n\tif (plugin && strncmp(plugin, \"function\", 8) == 0 &&\n\t    func_stack && !top_instance.filter_funcs)\n\t\tdie(\"Must supply function filtering with --func-stack\\n\");\n}\n\nstatic int __check_doing_something(struct buffer_instance *instance)\n{\n\treturn is_guest(instance) || (instance->flags & BUFFER_FL_PROFILE) ||\n\t\tinstance->plugin || instance->events || instance->get_procmap;\n}\n\nstatic void check_doing_something(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance) {\n\t\tif (__check_doing_something(instance))\n\t\t\treturn;\n\t}\n\n\tdie(\"no event or plugin was specified... aborting\");\n}\n\nstatic void\nupdate_plugin_instance(struct buffer_instance *instance,\n\t\t       enum trace_type type)\n{\n\tconst char *plugin = instance->plugin;\n\n\tif (is_guest(instance))\n\t\treturn;\n\n\tif (!plugin)\n\t\treturn;\n\n\tcheck_plugin(plugin);\n\n\t/*\n\t * Latency tracers just save the trace and kill\n\t * the threads.\n\t */\n\tif (strcmp(plugin, \"irqsoff\") == 0 ||\n\t    strcmp(plugin, \"preemptoff\") == 0 ||\n\t    strcmp(plugin, \"preemptirqsoff\") == 0 ||\n\t    strcmp(plugin, \"wakeup\") == 0 ||\n\t    strcmp(plugin, \"wakeup_rt\") == 0) {\n\t\tlatency = 1;\n\t\tif (host)\n\t\t\tdie(\"Network tracing not available with latency tracer plugins\");\n\t\tif (type & TRACE_TYPE_STREAM)\n\t\t\tdie(\"Streaming is not available with latency tracer plugins\");\n\t} else if (type == TRACE_TYPE_RECORD) {\n\t\tif (latency)\n\t\t\tdie(\"Can not record latency tracer and non latency trace together\");\n\t}\n\n\tif (fset < 0 && (strcmp(plugin, \"function\") == 0 ||\n\t\t\t strcmp(plugin, \"function_graph\") == 0))\n\t\tdie(\"function tracing not configured on this kernel\");\n\n\tif (type != TRACE_TYPE_EXTRACT)\n\t\tset_plugin_instance(instance, plugin);\n}\n\nstatic void update_plugins(enum trace_type type)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tupdate_plugin_instance(instance, type);\n}\n\nstatic void allocate_seq(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance) {\n\t\tinstance->s_save = malloc(sizeof(struct trace_seq) * instance->cpu_count);\n\t\tinstance->s_print = malloc(sizeof(struct trace_seq) * instance->cpu_count);\n\t\tif (!instance->s_save || !instance->s_print)\n\t\t\tdie(\"Failed to allocate instance info\");\n\t}\n}\n\n/* Find the overrun output, and add it to the print seq */\nstatic void add_overrun(int cpu, struct trace_seq *src, struct trace_seq *dst)\n{\n\tconst char overrun_str[] = \"overrun: \";\n\tconst char commit_overrun_str[] = \"commit overrun: \";\n\tconst char *p;\n\tint overrun;\n\tint commit_overrun;\n\n\tp = strstr(src->buffer, overrun_str);\n\tif (!p) {\n\t\t/* Warn? */\n\t\ttrace_seq_printf(dst, \"CPU %d: no overrun found?\\n\", cpu);\n\t\treturn;\n\t}\n\n\toverrun = atoi(p + strlen(overrun_str));\n\n\tp = strstr(p + 9, commit_overrun_str);\n\tif (p)\n\t\tcommit_overrun = atoi(p + strlen(commit_overrun_str));\n\telse\n\t\tcommit_overrun = -1;\n\n\tif (!overrun && !commit_overrun)\n\t\treturn;\n\n\ttrace_seq_printf(dst, \"CPU %d:\", cpu);\n\n\tif (overrun)\n\t\ttrace_seq_printf(dst, \" %d events lost\", overrun);\n\n\tif (commit_overrun)\n\t\ttrace_seq_printf(dst, \" %d events lost due to commit overrun\",\n\t\t\t\t commit_overrun);\n\n\ttrace_seq_putc(dst, '\\n');\n}\n\nstatic void record_stats(void)\n{\n\tstruct buffer_instance *instance;\n\tstruct trace_seq *s_save;\n\tstruct trace_seq *s_print;\n\tint cpu;\n\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance))\n\t\t\tcontinue;\n\n\t\ts_save = instance->s_save;\n\t\ts_print = instance->s_print;\n\t\tfor (cpu = 0; cpu < instance->cpu_count; cpu++) {\n\t\t\ttrace_seq_init(&s_save[cpu]);\n\t\t\ttrace_seq_init(&s_print[cpu]);\n\t\t\ttrace_seq_printf(&s_save[cpu], \"CPU: %d\\n\", cpu);\n\t\t\ttracecmd_stat_cpu_instance(instance, &s_save[cpu], cpu);\n\t\t\tadd_overrun(cpu, &s_save[cpu], &s_print[cpu]);\n\t\t}\n\t}\n}\n\nstatic void print_stats(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance)\n\t\tprint_stat(instance);\n}\n\nstatic void destroy_stats(void)\n{\n\tstruct buffer_instance *instance;\n\tint cpu;\n\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance))\n\t\t\tcontinue;\n\n\t\tfor (cpu = 0; cpu < instance->cpu_count; cpu++) {\n\t\t\ttrace_seq_destroy(&instance->s_save[cpu]);\n\t\t\ttrace_seq_destroy(&instance->s_print[cpu]);\n\t\t}\n\t}\n}\n\nstatic void list_event(const char *event)\n{\n\tstruct tracecmd_event_list *list;\n\n\tlist = malloc(sizeof(*list));\n\tif (!list)\n\t\tdie(\"Failed to allocate list for event\");\n\tlist->next = listed_events;\n\tlist->glob = event;\n\tlisted_events = list;\n}\n\n#define ALL_EVENTS \"*/*\"\n\nstatic void record_all_events(void)\n{\n\tstruct tracecmd_event_list *list;\n\n\twhile (listed_events) {\n\t\tlist = listed_events;\n\t\tlisted_events = list->next;\n\t\tfree(list);\n\t}\n\tlist = malloc(sizeof(*list));\n\tif (!list)\n\t\tdie(\"Failed to allocate list for all events\");\n\tlist->next = NULL;\n\tlist->glob = ALL_EVENTS;\n\tlisted_events = list;\n}\n\nstatic int recording_all_events(void)\n{\n\treturn listed_events && strcmp(listed_events->glob, ALL_EVENTS) == 0;\n}\n\nstatic void add_trigger(struct event_list *event, const char *trigger)\n{\n\tint ret;\n\n\tif (event->trigger) {\n\t\tevent->trigger = realloc(event->trigger,\n\t\t\t\t\t strlen(event->trigger) + strlen(\"\\n\") +\n\t\t\t\t\t strlen(trigger) + 1);\n\t\tstrcat(event->trigger, \"\\n\");\n\t\tstrcat(event->trigger, trigger);\n\t} else {\n\t\tret = asprintf(&event->trigger, \"%s\", trigger);\n\t\tif (ret < 0)\n\t\t\tdie(\"Failed to allocate event trigger\");\n\t}\n}\n\nstatic int test_stacktrace_trigger(struct buffer_instance *instance)\n{\n\tchar *path;\n\tint ret = 0;\n\tint fd;\n\n\tpath = tracefs_instance_get_file(instance->tracefs,\n\t\t\t\t\t \"events/sched/sched_switch/trigger\");\n\n\tclear_trigger(path);\n\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0)\n\t\tgoto out;\n\n\tret = write(fd, \"stacktrace\", 10);\n\tif (ret != 10)\n\t\tret = 0;\n\telse\n\t\tret = 1;\n\tclose(fd);\n out:\n\ttracefs_put_tracing_file(path);\n\n\treturn ret;\n}\n\nstatic int\nprofile_add_event(struct buffer_instance *instance, const char *event_str, int stack)\n{\n\tstruct event_list *event;\n\tchar buf[BUFSIZ];\n\tchar *p;\n\n\tstrcpy(buf, \"events/\");\n\tstrncpy(buf + 7, event_str, BUFSIZ - 7);\n\tbuf[BUFSIZ-1] = 0;\n\n\tif ((p = strstr(buf, \":\"))) {\n\t\t*p = '/';\n\t\tp++;\n\t}\n\n\tif (!trace_check_file_exists(instance, buf))\n\t\treturn -1;\n\n\t/* Only add event if it isn't already added */\n\tfor (event = instance->events; event; event = event->next) {\n\t\tif (p && strcmp(event->event, p) == 0)\n\t\t\tbreak;\n\t\tif (strcmp(event->event, event_str) == 0)\n\t\t\tbreak;\n\t}\n\n\tif (!event) {\n\t\tevent = malloc(sizeof(*event));\n\t\tif (!event)\n\t\t\tdie(\"Failed to allocate event\");\n\t\tmemset(event, 0, sizeof(*event));\n\t\tevent->event = event_str;\n\t\tadd_event(instance, event);\n\t}\n\n\tif (!recording_all_events())\n\t\tlist_event(event_str);\n\n\tif (stack) {\n\t\tif (!event->trigger || !strstr(event->trigger, \"stacktrace\"))\n\t\t\tadd_trigger(event, \"stacktrace\");\n\t}\n\n\treturn 0;\n}\n\nint tracecmd_add_event(const char *event_str, int stack)\n{\n\treturn profile_add_event(first_instance, event_str, stack);\n}\n\nstatic void enable_profile(struct buffer_instance *instance)\n{\n\tint stacktrace = 0;\n\tint i;\n\tchar *trigger_events[] = {\n\t\t\"sched:sched_switch\",\n\t\t\"sched:sched_wakeup\",\n\t\tNULL,\n\t};\n\tchar *events[] = {\n\t\t\"exceptions:page_fault_user\",\n\t\t\"irq:irq_handler_entry\",\n\t\t\"irq:irq_handler_exit\",\n\t\t\"irq:softirq_entry\",\n\t\t\"irq:softirq_exit\",\n\t\t\"irq:softirq_raise\",\n\t\t\"sched:sched_process_exec\",\n\t\t\"raw_syscalls\",\n\t\tNULL,\n\t};\n\n\tif (!instance->plugin) {\n\t\tif (trace_check_file_exists(instance, \"max_graph_depth\")) {\n\t\t\tinstance->plugin = \"function_graph\";\n\t\t\tset_max_graph_depth(instance, \"1\");\n\t\t} else\n\t\t\twarning(\"Kernel does not support max_graph_depth\\n\"\n\t\t\t\t\" Skipping user/kernel profiling\");\n\t}\n\n\tif (test_stacktrace_trigger(instance))\n\t\tstacktrace = 1;\n\telse\n\t\t/*\n\t\t * The stacktrace trigger is not implemented with this\n\t\t * kernel, then we need to default to the stack trace option.\n\t\t * This is less efficient but still works.\n\t\t */\n\t\tsave_option(instance, \"stacktrace\");\n\n\n\tfor (i = 0; trigger_events[i]; i++)\n\t\tprofile_add_event(instance, trigger_events[i], stacktrace);\n\n\tfor (i = 0; events[i]; i++)\n\t\tprofile_add_event(instance, events[i], 0);\n}\n\nstatic struct event_list *\ncreate_hook_event(struct buffer_instance *instance,\n\t\t  const char *system, const char *event)\n{\n\tstruct event_list *event_list;\n\tchar *event_name;\n\tint len;\n\n\tif (!system)\n\t\tsystem = \"*\";\n\n\tlen = strlen(event);\n\tlen += strlen(system) + 2;\n\n\tevent_name = malloc(len);\n\tif (!event_name)\n\t\tdie(\"Failed to allocate %s/%s\", system, event);\n\tsprintf(event_name, \"%s:%s\", system, event);\n\n\tevent_list = malloc(sizeof(*event_list));\n\tif (!event_list)\n\t\tdie(\"Failed to allocate event list for %s\", event_name);\n\tmemset(event_list, 0, sizeof(*event_list));\n\tevent_list->event = event_name;\n\tadd_event(instance, event_list);\n\n\tlist_event(event_name);\n\n\treturn event_list;\n}\n\nstatic void add_hook(struct buffer_instance *instance, const char *arg)\n{\n\tstruct event_list *event;\n\tstruct hook_list *hook;\n\n\thook = tracecmd_create_event_hook(arg);\n\tif (!hook)\n\t\tdie(\"Failed to create event hook %s\", arg);\n\n\thook->instance = instance;\n\thook->next = hooks;\n\thooks = hook;\n\n\t/* Make sure the event is enabled */\n\tevent = create_hook_event(instance, hook->start_system, hook->start_event);\n\tcreate_hook_event(instance, hook->end_system, hook->end_event);\n\n\tif (hook->stack) {\n\t\tif (!event->trigger || !strstr(event->trigger, \"stacktrace\"))\n\t\t\tadd_trigger(event, \"stacktrace\");\n\t}\n}\n\nvoid update_first_instance(struct buffer_instance *instance, int topt)\n{\n\tif (topt || instance == &top_instance)\n\t\tfirst_instance = &top_instance;\n\telse\n\t\tfirst_instance = buffer_instances;\n}\n\nvoid init_top_instance(void)\n{\n\tif (!top_instance.tracefs)\n\t\ttop_instance.tracefs = tracefs_instance_create(NULL);\n\ttop_instance.cpu_count = tracecmd_count_cpus();\n\ttop_instance.flags = BUFFER_FL_KEEP;\n\ttop_instance.trace_id = tracecmd_generate_traceid();\n\tinit_instance(&top_instance);\n}\n\n#define LONG_OPT(opt)\t\t(0x80 + ((opt) << 8))\n#define IS_LONG_OPT(opt)\t((opt) & 0x80)\n\nenum {\n\tOPT_compression\t\t= LONG_OPT(0),\n\tOPT_file_ver\t\t= LONG_OPT(1),\n\tOPT_verbose\t\t= LONG_OPT(2),\n\tOPT_tsc2nsec\t\t= LONG_OPT(3),\n\tOPT_fork\t\t= LONG_OPT(4),\n\tOPT_tsyncinterval\t= LONG_OPT(5),\n\tOPT_user\t\t= LONG_OPT(6),\n\tOPT_procmap\t\t= LONG_OPT(7),\n\tOPT_quiet\t\t= LONG_OPT(8),\n\tOPT_debug\t\t= LONG_OPT(9),\n\tOPT_no_filter\t\t= LONG_OPT(10),\n\tOPT_max_graph_depth\t= LONG_OPT(11),\n\tOPT_tsoffset\t\t= LONG_OPT(12),\n\tOPT_bycomm\t\t= LONG_OPT(13),\n\tOPT_stderr\t\t= LONG_OPT(14),\n\tOPT_profile\t\t= LONG_OPT(15),\n\tOPT_nosplice\t\t= LONG_OPT(16),\n\tOPT_funcstack\t\t= LONG_OPT(17),\n\tOPT_date\t\t= LONG_OPT(18),\n\tOPT_module\t\t= LONG_OPT(19),\n\tOPT_nofifos\t\t= LONG_OPT(20),\n\tOPT_cmdlines_size\t= LONG_OPT(21),\n\tOPT_poll\t\t= LONG_OPT(22),\n\tOPT_name\t\t= LONG_OPT(23),\n\tOPT_proxy\t\t= LONG_OPT(24),\n\tOPT_temp\t\t= LONG_OPT(25),\n\tOPT_notimeout\t\t= LONG_OPT(26),\n\tOPT_daemonize\t\t= LONG_OPT(27),\n\tOPT_subbuf\t\t= LONG_OPT(28),\n};\n\nvoid trace_stop(int argc, char **argv)\n{\n\tint topt = 0;\n\tstruct buffer_instance *instance = &top_instance;\n\n\tinit_top_instance();\n\n\tfor (;;) {\n\t\tint c;\n\n\t\tc = getopt(argc-1, argv+1, \"hatB:\");\n\t\tif (c == -1)\n\t\t\tbreak;\n\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tinstance = allocate_instance(optarg);\n\t\t\tif (!instance)\n\t\t\t\tdie(\"Failed to create instance\");\n\t\t\tadd_instance(instance, local_cpu_count);\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tadd_all_instances();\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\t/* Force to use top instance */\n\t\t\ttopt = 1;\n\t\t\tinstance = &top_instance;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\tupdate_first_instance(instance, topt);\n\ttracecmd_disable_tracing();\n\texit(0);\n}\n\nvoid trace_restart(int argc, char **argv)\n{\n\tint topt = 0;\n\tstruct buffer_instance *instance = &top_instance;\n\n\tinit_top_instance();\n\n\tfor (;;) {\n\t\tint c;\n\n\t\tc = getopt(argc-1, argv+1, \"hatB:\");\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tinstance = allocate_instance(optarg);\n\t\t\tif (!instance)\n\t\t\t\tdie(\"Failed to create instance\");\n\t\t\tadd_instance(instance, local_cpu_count);\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tadd_all_instances();\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\t/* Force to use top instance */\n\t\t\ttopt = 1;\n\t\t\tinstance = &top_instance;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\n\t}\n\tupdate_first_instance(instance, topt);\n\ttracecmd_enable_tracing();\n\texit(0);\n}\n\n/**\n * find_dynevent_type - Find string formatted dynamic event type\n *\t\t\tin \"enum tracefs_dynevent_type\".\n *\n * @type: Dynamic event type in string format.\n *\n * Returns an unsigned int value for the event specified in @type.\n */\nstatic unsigned int find_dynevent_type(const char *type)\n{\n\t/* WARN: Should be kept in sync with \"enum tracefs_dynevent_type\". */\n\tif (!strcmp(type, \"kprobe\"))\n\t\treturn TRACEFS_DYNEVENT_KPROBE;\n\telse if (!strcmp(type, \"kretprobe\"))\n\t\treturn TRACEFS_DYNEVENT_KRETPROBE;\n\telse if (!strcmp(type, \"uprobe\"))\n\t\treturn TRACEFS_DYNEVENT_UPROBE;\n\telse if (!strcmp(type, \"uretprobe\"))\n\t\treturn TRACEFS_DYNEVENT_URETPROBE;\n\telse if (!strcmp(type, \"eprobe\"))\n\t\treturn TRACEFS_DYNEVENT_EPROBE;\n\telse if (!strcmp(type, \"synth\"))\n\t\treturn TRACEFS_DYNEVENT_SYNTH;\n\telse if (!strcmp(type, \"all\"))\n\t\t/*\n\t\t * Unfortunately TRACEFS_DYNEVENT_ALL does not work here.\n\t\t * Because tracefs_dynevent_destroy_all() assumes 0 means all\n\t\t * and destroys all dynamic events.\n\t\t */\n\t\treturn TRACEFS_DYNEVENT_KPROBE |\n\t\t       TRACEFS_DYNEVENT_KRETPROBE |\n\t\t       TRACEFS_DYNEVENT_UPROBE |\n\t\t       TRACEFS_DYNEVENT_URETPROBE |\n\t\t       TRACEFS_DYNEVENT_EPROBE |\n\t\t       TRACEFS_DYNEVENT_SYNTH;\n\telse\n\t\tdie(\"Invalid event type '%s'!\\n\", type);\n}\n\nvoid trace_reset(int argc, char **argv)\n{\n\tint c;\n\tint topt = 0;\n\tstruct buffer_instance *instance = &top_instance;\n\tunsigned int excluded_types = 0;\n\n\tinit_top_instance();\n\n\t/* if last arg is -a, then -b and -d apply to all instances */\n\tint last_specified_all = 0;\n\tstruct buffer_instance *inst; /* iterator */\n\n\twhile ((c = getopt(argc-1, argv+1, \"hab:B:tdk:\")) >= 0) {\n\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'b':\n\t\t{\n\t\t\tint size = atoi(optarg);\n\t\t\t/* Min buffer size is 1 */\n\t\t\tif (size <= 1)\n\t\t\t\tsize = 1;\n\t\t\tif (last_specified_all) {\n\t\t\t\tfor_each_instance(inst) {\n\t\t\t\t\tinst->buffer_size = size;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tinstance->buffer_size = size;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase 'B':\n\t\t\tlast_specified_all = 0;\n\t\t\tinstance = allocate_instance(optarg);\n\t\t\tif (!instance)\n\t\t\t\tdie(\"Failed to create instance\");\n\t\t\tadd_instance(instance, local_cpu_count);\n\t\t\t/* -d will remove keep */\n\t\t\tinstance->flags |= BUFFER_FL_KEEP;\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\t/* Force to use top instance */\n\t\t\tlast_specified_all = 0;\n\t\t\ttopt = 1;\n\t\t\tinstance = &top_instance;\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tlast_specified_all = 1;\n\t\t\tadd_all_instances();\n\t\t\tfor_each_instance(inst) {\n\t\t\t\tinst->flags |= BUFFER_FL_KEEP;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\tif (last_specified_all) {\n\t\t\t\tfor_each_instance(inst) {\n\t\t\t\t\tinst->flags &= ~BUFFER_FL_KEEP;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (is_top_instance(instance))\n\t\t\t\t\tdie(\"Can not delete top level buffer\");\n\t\t\t\tinstance->flags &= ~BUFFER_FL_KEEP;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'k':\n\t\t\texcluded_types |= find_dynevent_type(optarg);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\t}\n\t}\n\tupdate_first_instance(instance, topt);\n\ttracecmd_disable_all_tracing(1);\n\tset_buffer_size();\n\tclear_filters();\n\tclear_triggers();\n\tclear_all_dynamic_events(excluded_types);\n\tclear_error_log();\n\t/* set clock to \"local\" */\n\treset_clock();\n\treset_event_pid();\n\treset_max_latency_instance();\n\treset_cpu_mask();\n\ttracecmd_remove_instances();\n\tclear_func_filters();\n\t/* restore tracing_on to 1 */\n\ttracecmd_enable_tracing();\n\texit(0);\n}\n\nstatic void init_common_record_context(struct common_record_context *ctx,\n\t\t\t\t       enum trace_cmd curr_cmd)\n{\n\tmemset(ctx, 0, sizeof(*ctx));\n\tctx->instance = &top_instance;\n\tctx->curr_cmd = curr_cmd;\n\tlocal_cpu_count = tracecmd_count_cpus();\n\tctx->file_version = tracecmd_default_file_version();\n\tinit_top_instance();\n}\n\nstatic void add_argv(struct buffer_instance *instance, char *arg, bool prepend)\n{\n\tinstance->argv = realloc(instance->argv,\n\t\t\t\t (instance->argc + 1) * sizeof(char *));\n\tif (!instance->argv)\n\t\tdie(\"Can not allocate instance args\");\n\tif (prepend) {\n\t\tmemmove(instance->argv + 1, instance->argv,\n\t\t\tinstance->argc * sizeof(*instance->argv));\n\t\tinstance->argv[0] = arg;\n\t} else {\n\t\tinstance->argv[instance->argc] = arg;\n\t}\n\tinstance->argc++;\n}\n\nstatic void add_arg(struct buffer_instance *instance,\n\t\t    int c, const char *opts,\n\t\t    struct option *long_options, char *optarg)\n{\n\tchar *ptr, *arg;\n\tint i, ret;\n\n\t/* Short or long arg */\n\tif (!IS_LONG_OPT(c)) {\n\t\tptr = strchr(opts, c);\n\t\tif (!ptr)\n\t\t\treturn; /* Not found? */\n\t\tret = asprintf(&arg, \"-%c\", c);\n\t\tif (ret < 0)\n\t\t\tdie(\"Can not allocate argument\");\n\t\tadd_argv(instance, arg, false);\n\t\tif (ptr[1] == ':') {\n\t\t\targ = strdup(optarg);\n\t\t\tif (!arg)\n\t\t\t\tdie(\"Can not allocate arguments\");\n\t\t\tadd_argv(instance, arg, false);\n\t\t}\n\t\treturn;\n\t}\n\tfor (i = 0; long_options[i].name; i++) {\n\t\tif (c != long_options[i].val)\n\t\t\tcontinue;\n\t\tret = asprintf(&arg, \"--%s\", long_options[i].name);\n\t\tif (ret < 0)\n\t\t\tdie(\"Can not allocate argument\");\n\t\tadd_argv(instance, arg, false);\n\t\tif (long_options[i].has_arg) {\n\t\t\targ = strdup(optarg);\n\t\t\tif (!arg)\n\t\t\t\tdie(\"Can not allocate arguments\");\n\t\t\tadd_argv(instance, arg, false);\n\t\t}\n\t\treturn;\n\t}\n\t/* Not found? */\n}\n\nstatic inline void cmd_check_die(struct common_record_context *ctx,\n\t\t\t\t enum trace_cmd id, char *cmd, char *param)\n{\n\tif (ctx->curr_cmd == id)\n\t\tdie(\"%s has no effect with the command %s\\n\"\n\t\t    \"Did you mean 'record'?\", param, cmd);\n}\n\nstatic inline void remove_instances(struct buffer_instance *instances)\n{\n\tstruct buffer_instance *del;\n\n\twhile (instances) {\n\t\tdel = instances;\n\t\tinstances = instances->next;\n\t\tfree(del->name);\n\t\tif (tracefs_instance_is_new(del->tracefs))\n\t\t\ttracefs_instance_destroy(del->tracefs);\n\t\ttracefs_instance_free(del->tracefs);\n\t\tfree(del);\n\t}\n}\n\nstatic inline void\ncheck_instance_die(struct buffer_instance *instance, char *param)\n{\n\tif (instance->delete)\n\t\tdie(\"Instance %s is marked for deletion, invalid option %s\",\n\t\t    tracefs_instance_get_name(instance->tracefs), param);\n}\n\nstatic bool clock_is_supported(struct tracefs_instance *instance, const char *clock)\n{\n\tchar *all_clocks = NULL;\n\tchar *ret = NULL;\n\n\tall_clocks  = tracefs_instance_file_read(instance, \"trace_clock\", NULL);\n\tif (!all_clocks)\n\t\treturn false;\n\n\tret = strstr(all_clocks, clock);\n\tif (ret && (ret == all_clocks || ret[-1] == ' ' || ret[-1] == '[')) {\n\t\tswitch (ret[strlen(clock)]) {\n\t\tcase ' ':\n\t\tcase '\\0':\n\t\tcase ']':\n\t\tcase '\\n':\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tret = NULL;\n\t\t}\n\t} else {\n\t\tret = NULL;\n\t}\n\tfree(all_clocks);\n\n\treturn ret != NULL;\n}\n\n#ifdef PERF\nstatic int get_tsc_nsec(int *shift, int *mult)\n{\n\tstatic int cpu_shift, cpu_mult;\n\tstatic int supported;\n\tint cpus = tracecmd_count_cpus();\n\tstruct trace_perf perf;\n\tint i;\n\n\tif (supported)\n\t\tgoto out;\n\n\tsupported = -1;\n\tif (tcmd_perf_init(&perf, 1, 0, getpid()))\n\t\treturn -1;\n\tif (tcmd_perf_open(&perf))\n\t\treturn -1;\n\tcpu_shift = perf.mmap->time_shift;\n\tcpu_mult = perf.mmap->time_mult;\n\tfor (i = 1; i < cpus; i++) {\n\t\ttcmd_perf_close(&perf);\n\t\tif (tcmd_perf_init(&perf, 1, i, getpid()))\n\t\t\tbreak;\n\t\tif (tcmd_perf_open(&perf))\n\t\t\tbreak;\n\t\tif (perf.mmap->time_shift != cpu_shift ||\n\t\t    perf.mmap->time_mult != cpu_mult) {\n\t\t\twarning(\"Found different TSC multiplier and shift for CPU %d: %d;%d instead of %d;%d\",\n\t\t\t\ti, perf.mmap->time_mult, perf.mmap->time_shift, cpu_mult, cpu_shift);\n\t\t\tbreak;\n\t\t}\n\t}\n\ttcmd_perf_close(&perf);\n\tif (i < cpus)\n\t\treturn -1;\n\n\tif (cpu_shift || cpu_mult)\n\t\tsupported = 1;\nout:\n\tif (supported < 0)\n\t\treturn -1;\n\n\tif (shift)\n\t\t*shift = cpu_shift;\n\tif (mult)\n\t\t*mult = cpu_mult;\n\n\treturn 0;\n}\n#else\nstatic int get_tsc_nsec(int *shift, int *mult)\n{\n\treturn -1;\n}\n#endif\n\nbool trace_tsc2nsec_is_supported(void)\n{\n\treturn get_tsc_nsec(NULL, NULL) == 0;\n}\n\nstatic void parse_record_options(int argc,\n\t\t\t\t char **argv,\n\t\t\t\t enum trace_cmd curr_cmd,\n\t\t\t\t struct common_record_context *ctx)\n{\n\tconst char *plugin = NULL;\n\tconst char *option;\n\tstruct event_list *event = NULL;\n\tstruct event_list *last_event = NULL;\n\tstruct addrinfo *result;\n\tchar *pids;\n\tchar *pid;\n\tchar *sav;\n\tint name_counter = 0;\n\tint negative = 0;\n\tbool is_proxy = false;\n\tstruct buffer_instance *instance, *del_list = NULL;\n\tint do_children = 0;\n\tint fpids_count = 0;\n\n\tinit_common_record_context(ctx, curr_cmd);\n\n\tfor (;;) {\n\t\tint option_index = 0;\n\t\tint ret;\n\t\tint c;\n\t\tconst char *opts;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"date\", no_argument, NULL, OPT_date},\n\t\t\t{\"func-stack\", no_argument, NULL, OPT_funcstack},\n\t\t\t{\"nosplice\", no_argument, NULL, OPT_nosplice},\n\t\t\t{\"nofifos\", no_argument, NULL, OPT_nofifos},\n\t\t\t{\"profile\", no_argument, NULL, OPT_profile},\n\t\t\t{\"stderr\", no_argument, NULL, OPT_stderr},\n\t\t\t{\"by-comm\", no_argument, NULL, OPT_bycomm},\n\t\t\t{\"ts-offset\", required_argument, NULL, OPT_tsoffset},\n\t\t\t{\"max-graph-depth\", required_argument, NULL, OPT_max_graph_depth},\n\t\t\t{\"cmdlines-size\", required_argument, NULL, OPT_cmdlines_size},\n\t\t\t{\"no-filter\", no_argument, NULL, OPT_no_filter},\n\t\t\t{\"debug\", no_argument, NULL, OPT_debug},\n\t\t\t{\"notimeout\", no_argument, NULL, OPT_notimeout},\n\t\t\t{\"quiet\", no_argument, NULL, OPT_quiet},\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{\"proc-map\", no_argument, NULL, OPT_procmap},\n\t\t\t{\"user\", required_argument, NULL, OPT_user},\n\t\t\t{\"module\", required_argument, NULL, OPT_module},\n\t\t\t{\"tsync-interval\", required_argument, NULL, OPT_tsyncinterval},\n\t\t\t{\"fork\", no_argument, NULL, OPT_fork},\n\t\t\t{\"tsc2nsec\", no_argument, NULL, OPT_tsc2nsec},\n\t\t\t{\"poll\", no_argument, NULL, OPT_poll},\n\t\t\t{\"name\", required_argument, NULL, OPT_name},\n\t\t\t{\"verbose\", optional_argument, NULL, OPT_verbose},\n\t\t\t{\"compression\", required_argument, NULL, OPT_compression},\n\t\t\t{\"file-version\", required_argument, NULL, OPT_file_ver},\n\t\t\t{\"proxy\", required_argument, NULL, OPT_proxy},\n\t\t\t{\"temp\", required_argument, NULL, OPT_temp},\n\t\t\t{\"subbuf-size\", required_argument, NULL, OPT_subbuf},\n\t\t\t{\"daemonize\", no_argument, NULL, OPT_daemonize},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tif (IS_EXTRACT(ctx))\n\t\t\topts = \"+haf:Fp:co:O:sr:g:l:n:P:N:tb:B:ksiT\";\n\t\telse\n\t\t\topts = \"+hae:f:FA:p:cC:dDGo:O:s:r:V:vg:l:n:P:N:tb:R:B:kKsSiTm:M:H:q\";\n\t\tc = getopt_long (argc-1, argv+1, opts, long_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\n\t\t/*\n\t\t * If the current instance is to record a guest, then save\n\t\t * all the arguments for this instance.\n\t\t */\n\t\tif (c != 'B' && (c != 'A' || is_proxy) && c != OPT_name &&\n\t\t    is_guest(ctx->instance) && c != OPT_proxy) {\n\t\t\tadd_arg(ctx->instance, c, opts, long_options, optarg);\n\t\t\tif (c == 'C')\n\t\t\t\tctx->instance->flags |= BUFFER_FL_HAS_CLOCK;\n\t\t\tcontinue;\n\t\t}\n\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-a\");\n\t\t\tif (IS_EXTRACT(ctx)) {\n\t\t\t\tadd_all_instances();\n\t\t\t} else {\n\t\t\t\tctx->record_all = 1;\n\t\t\t\trecord_all_events();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'e':\n\t\t\tcheck_instance_die(ctx->instance, \"-e\");\n\t\t\tctx->events = 1;\n\t\t\tevent = malloc(sizeof(*event));\n\t\t\tif (!event)\n\t\t\t\tdie(\"Failed to allocate event %s\", optarg);\n\t\t\tmemset(event, 0, sizeof(*event));\n\t\t\tevent->event = optarg;\n\t\t\tadd_event(ctx->instance, event);\n\t\t\tevent->neg = negative;\n\t\t\tevent->filter = NULL;\n\t\t\tlast_event = event;\n\n\t\t\tif (!ctx->record_all)\n\t\t\t\tlist_event(optarg);\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tif (!last_event)\n\t\t\t\tdie(\"filter must come after event\");\n\t\t\tif (last_event->filter) {\n\t\t\t\tlast_event->filter =\n\t\t\t\t\trealloc(last_event->filter,\n\t\t\t\t\t\tstrlen(last_event->filter) +\n\t\t\t\t\t\tstrlen(\"&&()\") +\n\t\t\t\t\t\tstrlen(optarg) + 1);\n\t\t\t\tstrcat(last_event->filter, \"&&(\");\n\t\t\t\tstrcat(last_event->filter, optarg);\n\t\t\t\tstrcat(last_event->filter, \")\");\n\t\t\t} else {\n\t\t\t\tret = asprintf(&last_event->filter, \"(%s)\", optarg);\n\t\t\t\tif (ret < 0)\n\t\t\t\t\tdie(\"Failed to allocate filter %s\", optarg);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'R':\n\t\t\tif (!last_event)\n\t\t\t\tdie(\"trigger must come after event\");\n\t\t\tadd_trigger(event, optarg);\n\t\t\tbreak;\n\n\t\tcase OPT_name:\n\t\t\tif (!ctx->instance)\n\t\t\t\tdie(\"No instance defined for name option\\n\");\n\t\t\tif (!is_guest(ctx->instance))\n\t\t\t\tdie(\"  --name is only used for -A options\\n\");\n\t\t\tfree(ctx->instance->name);\n\t\t\tctx->instance->name = strdup(optarg);\n\t\t\tif (!ctx->instance->name)\n\t\t\t\tdie(\"Failed to allocate name\");\n\t\t\tbreak;\n\n\t\tcase OPT_proxy:\n\t\t\tis_proxy = true;\n\t\t\t/* fall through */\n\t\tcase 'A': {\n\t\t\tchar *name = NULL;\n\t\t\tint cid = -1, port = -1;\n\n\t\t\tif (!IS_RECORD(ctx))\n\t\t\t\tdie(\"%s is only allowed for record operations\",\n\t\t\t\t    is_proxy ? \"--proxy\" : \"-A\");\n\n\t\t\tname = parse_guest_name(optarg, &cid, &port, &result);\n\t\t\tif (cid == -1 && !result)\n\t\t\t\tdie(\"guest %s not found\", optarg);\n\t\t\tif (port == -1)\n\t\t\t\tport = TRACE_AGENT_DEFAULT_PORT;\n\t\t\tif (!name || !*name) {\n\t\t\t\tret = asprintf(&name, \"unnamed-%d\", name_counter++);\n\t\t\t\tif (ret < 0)\n\t\t\t\t\tname = NULL;\n\t\t\t} else {\n\t\t\t\t/* Needs to be allocate */\n\t\t\t\tname = strdup(name);\n\t\t\t}\n\t\t\tif (!name)\n\t\t\t\tdie(\"Failed to allocate guest name\");\n\n\t\t\tctx->instance = allocate_instance(name);\n\t\t\tif (!ctx->instance)\n\t\t\t\tdie(\"Failed to allocate instance\");\n\n\t\t\tif (result) {\n\t\t\t\tctx->instance->flags |= BUFFER_FL_NETWORK;\n\t\t\t\tctx->instance->port_type = USE_TCP;\n\t\t\t}\n\n\t\t\tif (is_proxy)\n\t\t\t\tctx->instance->flags |= BUFFER_FL_PROXY;\n\n\t\t\tctx->instance->flags |= BUFFER_FL_GUEST;\n\t\t\tctx->instance->result = result;\n\t\t\tctx->instance->cid = cid;\n\t\t\tctx->instance->port = port;\n\t\t\tctx->instance->name = name;\n\t\t\tadd_instance(ctx->instance, 0);\n\t\t\tctx->data_flags |= DATA_FL_GUEST;\n\n\t\t\t/* Do not send a clock to a proxy */\n\t\t\tif (is_proxy)\n\t\t\t\tctx->instance->flags |= BUFFER_FL_HAS_CLOCK;\n\n\t\t\tbreak;\n\t\t}\n\t\tcase 'F':\n\t\t\ttest_set_event_pid(ctx->instance);\n\t\t\tfilter_task = 1;\n\t\t\tbreak;\n\t\tcase 'G':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-G\");\n\t\t\tctx->global = 1;\n\t\t\tbreak;\n\t\tcase 'P':\n\t\t\tcheck_instance_die(ctx->instance, \"-P\");\n\t\t\ttest_set_event_pid(ctx->instance);\n\t\t\tpids = strdup(optarg);\n\t\t\tif (!pids)\n\t\t\t\tdie(\"strdup\");\n\t\t\tpid = strtok_r(pids, \",\", &sav);\n\t\t\twhile (pid) {\n\t\t\t\tfpids_count += add_filter_pid(ctx->instance,\n\t\t\t\t\t\t\t      atoi(pid), 0);\n\t\t\t\tpid = strtok_r(NULL, \",\", &sav);\n\t\t\t\tctx->instance->nr_process_pids++;\n\t\t\t}\n\t\t\tctx->instance->process_pids = ctx->instance->filter_pids;\n\t\t\tfree(pids);\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\tcheck_instance_die(ctx->instance, \"-c\");\n\t\t\ttest_set_event_pid(ctx->instance);\n\t\t\tdo_children = 1;\n\t\t\tif (!ctx->instance->have_event_fork) {\n#ifdef NO_PTRACE\n\t\t\t\tdie(\"-c invalid: ptrace not supported\");\n#endif\n\t\t\t\tdo_ptrace = 1;\n\t\t\t\tctx->instance->ptrace_child = 1;\n\n\t\t\t} else {\n\t\t\t\tsave_option(ctx->instance, \"event-fork\");\n\t\t\t}\n\t\t\tif (ctx->instance->have_func_fork)\n\t\t\t\tsave_option(ctx->instance, \"function-fork\");\n\t\t\tbreak;\n\t\tcase 'C':\n\t\t\tcheck_instance_die(ctx->instance, \"-C\");\n\t\t\tif (strcmp(optarg, TSCNSEC_CLOCK) == 0) {\n\t\t\t\tret = get_tsc_nsec(&ctx->tsc2nsec.shift,\n\t\t\t\t\t\t   &ctx->tsc2nsec.mult);\n\t\t\t\tif (ret)\n\t\t\t\t\tdie(\"TSC to nanosecond is not supported\");\n\t\t\t\tctx->instance->flags |= BUFFER_FL_TSC2NSEC;\n\t\t\t\tctx->instance->clock = TSC_CLOCK;\n\t\t\t} else {\n\t\t\t\tctx->instance->clock = optarg;\n\t\t\t}\n\t\t\tif (!clock_is_supported(NULL, ctx->instance->clock))\n\t\t\t\tdie(\"Clock %s is not supported\", ctx->instance->clock);\n\t\t\tctx->instance->clock = strdup(ctx->instance->clock);\n\t\t\tif (!ctx->instance->clock)\n\t\t\t\tdie(\"Failed allocation\");\n\t\t\tctx->instance->flags |= BUFFER_FL_HAS_CLOCK;\n\t\t\tif (!ctx->clock && !is_guest(ctx->instance))\n\t\t\t\tctx->clock = ctx->instance->clock;\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\tnegative = 1;\n\t\t\tbreak;\n\t\tcase 'l':\n\t\t\tadd_func(&ctx->instance->filter_funcs,\n\t\t\t\t ctx->instance->filter_mod, optarg);\n\t\t\tctx->filtered = 1;\n\t\t\tbreak;\n\t\tcase 'n':\n\t\t\tcheck_instance_die(ctx->instance, \"-n\");\n\t\t\tadd_func(&ctx->instance->notrace_funcs,\n\t\t\t\t ctx->instance->filter_mod, optarg);\n\t\t\tctx->filtered = 1;\n\t\t\tbreak;\n\t\tcase 'g':\n\t\t\tcheck_instance_die(ctx->instance, \"-g\");\n\t\t\tadd_func(&graph_funcs, ctx->instance->filter_mod, optarg);\n\t\t\tctx->filtered = 1;\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tcheck_instance_die(ctx->instance, \"-p\");\n\t\t\tif (ctx->instance->plugin)\n\t\t\t\tdie(\"only one plugin allowed\");\n\t\t\tfor (plugin = optarg; isspace(*plugin); plugin++)\n\t\t\t\t;\n\t\t\tctx->instance->plugin = plugin;\n\t\t\tfor (optarg += strlen(optarg) - 1;\n\t\t\t     optarg > plugin && isspace(*optarg); optarg--)\n\t\t\t\t;\n\t\t\toptarg++;\n\t\t\toptarg[0] = '\\0';\n\t\t\tbreak;\n\t\tcase 'D':\n\t\t\tctx->total_disable = 1;\n\t\t\t/* fall through */\n\t\tcase 'd':\n\t\t\tctx->disable = 1;\n\t\t\tbreak;\n\t\tcase 'o':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-o\");\n\t\t\tif (IS_RECORD_AGENT(ctx))\n\t\t\t\tdie(\"-o incompatible with agent recording\");\n\t\t\tif (host)\n\t\t\t\tdie(\"-o incompatible with -N\");\n\t\t\tif (IS_START(ctx))\n\t\t\t\tdie(\"start does not take output\\n\"\n\t\t\t\t    \"Did you mean 'record'?\");\n\t\t\tif (IS_STREAM(ctx))\n\t\t\t\tdie(\"stream does not take output\\n\"\n\t\t\t\t    \"Did you mean 'record'?\");\n\t\t\tif (ctx->output)\n\t\t\t\tdie(\"only one output file allowed\");\n\t\t\tctx->output = optarg;\n\n\t\t\tif (IS_PROFILE(ctx)) {\n\t\t\t\tint fd;\n\n\t\t\t\t/* pipe the output to this file instead of stdout */\n\t\t\t\tsave_stdout = dup(1);\n\t\t\t\tclose(1);\n\t\t\t\tfd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, 0644);\n\t\t\t\tif (fd < 0)\n\t\t\t\t\tdie(\"can't write to %s\", optarg);\n\t\t\t\tif (fd != 1) {\n\t\t\t\t\tdup2(fd, 1);\n\t\t\t\t\tclose(fd);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OPT_temp:\n\t\t\tif (ctx->temp)\n\t\t\t\tdie(\"Only one temp directory can be listed\");\n\t\t\tctx->temp = optarg;\n\t\t\tbreak;\n\t\tcase 'O':\n\t\t\tcheck_instance_die(ctx->instance, \"-O\");\n\t\t\toption = optarg;\n\t\t\tsave_option(ctx->instance, option);\n\t\t\tbreak;\n\t\tcase 'T':\n\t\t\tcheck_instance_die(ctx->instance, \"-T\");\n\t\t\tsave_option(ctx->instance, \"stacktrace\");\n\t\t\tbreak;\n\t\tcase 'H':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-H\");\n\t\t\tcheck_instance_die(ctx->instance, \"-H\");\n\t\t\tadd_hook(ctx->instance, optarg);\n\t\t\tctx->events = 1;\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-s\");\n\t\t\tif (IS_EXTRACT(ctx)) {\n\t\t\t\tif (optarg)\n\t\t\t\t\tusage(argv);\n\t\t\t\trecorder_flags |= TRACECMD_RECORD_SNAPSHOT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!optarg)\n\t\t\t\tusage(argv);\n\t\t\tsleep_time = atoi(optarg);\n\t\t\tbreak;\n\t\tcase 'S':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-S\");\n\t\t\tctx->manual = 1;\n\t\t\t/* User sets events for profiling */\n\t\t\tif (!event)\n\t\t\t\tctx->events = 0;\n\t\t\tbreak;\n\t\tcase 'r':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-r\");\n\t\t\trt_prio = atoi(optarg);\n\t\t\tbreak;\n\t\tcase 'N':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-N\");\n\t\t\tif (!IS_RECORD(ctx))\n\t\t\t\tdie(\"-N only available with record\");\n\t\t\tif (IS_RECORD_AGENT(ctx))\n\t\t\t\tdie(\"-N incompatible with agent recording\");\n\t\t\tif (ctx->output)\n\t\t\t\tdie(\"-N incompatible with -o\");\n\t\t\thost = optarg;\n\t\t\tbreak;\n\t\tcase 'V':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-V\");\n\t\t\tif (!IS_RECORD(ctx))\n\t\t\t\tdie(\"-V only available with record\");\n\t\t\tif (IS_RECORD_AGENT(ctx))\n\t\t\t\tdie(\"-V incompatible with agent recording\");\n\t\t\tif (ctx->output)\n\t\t\t\tdie(\"-V incompatible with -o\");\n\t\t\thost = optarg;\n\t\t\tctx->instance->port_type = USE_VSOCK;\n\t\t\tbreak;\n\t\tcase 'm':\n\t\t\tif (max_kb)\n\t\t\t\tdie(\"-m can only be specified once\");\n\t\t\tif (!IS_RECORD(ctx))\n\t\t\t\tdie(\"only record take 'm' option\");\n\t\t\tmax_kb = atoi(optarg);\n\t\t\tbreak;\n\t\tcase 'M':\n\t\t\tcheck_instance_die(ctx->instance, \"-M\");\n\t\t\tctx->instance->cpumask = alloc_mask_from_hex(ctx->instance, optarg);\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-t\");\n\t\t\tif (IS_EXTRACT(ctx))\n\t\t\t\tctx->topt = 1; /* Extract top instance also */\n\t\t\telse\n\t\t\t\tctx->instance->port_type = USE_TCP;\n\t\t\tbreak;\n\t\tcase 'b':\n\t\t\tcheck_instance_die(ctx->instance, \"-b\");\n\t\t\tctx->instance->buffer_size = atoi(optarg);\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\t/* Turn off proxy for the next options */\n\t\t\tis_proxy = false;\n\t\t\tctx->instance = allocate_instance(optarg);\n\t\t\tif (!ctx->instance)\n\t\t\t\tdie(\"Failed to create instance\");\n\t\t\tif (IS_CMDSET(ctx))\n\t\t\t\tctx->instance->delete = negative;\n\t\t\tnegative = 0;\n\t\t\tif (ctx->instance->delete) {\n\t\t\t\tctx->instance->next = del_list;\n\t\t\t\tdel_list = ctx->instance;\n\t\t\t} else\n\t\t\t\tadd_instance(ctx->instance, local_cpu_count);\n\t\t\tif (IS_PROFILE(ctx))\n\t\t\t\tctx->instance->flags |= BUFFER_FL_PROFILE;\n\t\t\tbreak;\n\t\tcase 'K':\n\t\t\tKeep = 1;\n\t\t\t/* fall through */\n\t\tcase 'k':\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"-k\");\n\t\t\tkeep = 1;\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\tignore_event_not_found = 1;\n\t\t\tbreak;\n\t\tcase OPT_user:\n\t\t\tctx->user = strdup(optarg);\n\t\t\tif (!ctx->user)\n\t\t\t\tdie(\"Failed to allocate user name\");\n\t\t\tbreak;\n\t\tcase OPT_procmap:\n\t\t\tcmd_check_die(ctx, CMD_start, *(argv+1), \"--proc-map\");\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--proc-map\");\n\t\t\tcheck_instance_die(ctx->instance, \"--proc-map\");\n\t\t\tctx->instance->get_procmap = 1;\n\t\t\tbreak;\n\t\tcase OPT_date:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--date\");\n\t\t\tctx->date = 1;\n\t\t\tif (ctx->data_flags & DATA_FL_OFFSET)\n\t\t\t\tdie(\"Can not use both --date and --ts-offset\");\n\t\t\tctx->data_flags |= DATA_FL_DATE;\n\t\t\tbreak;\n\t\tcase OPT_funcstack:\n\t\t\tfunc_stack = 1;\n\t\t\tbreak;\n\t\tcase OPT_nosplice:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--nosplice\");\n\t\t\trecorder_flags |= TRACECMD_RECORD_NOSPLICE;\n\t\t\tbreak;\n\t\tcase OPT_nofifos:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--nofifos\");\n\t\t\tno_fifos = true;\n\t\t\tbreak;\n\t\tcase OPT_profile:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--profile\");\n\t\t\tcheck_instance_die(ctx->instance, \"--profile\");\n\t\t\thandle_init = trace_init_profile;\n\t\t\tctx->instance->flags |= BUFFER_FL_PROFILE;\n\t\t\tctx->events = 1;\n\t\t\tbreak;\n\t\tcase OPT_stderr:\n\t\t\t/* if -o was used (for profile), ignore this */\n\t\t\tif (save_stdout >= 0)\n\t\t\t\tbreak;\n\t\t\tsave_stdout = dup(1);\n\t\t\tclose(1);\n\t\t\tdup2(2, 1);\n\t\t\tbreak;\n\t\tcase OPT_bycomm:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--by-comm\");\n\t\t\ttrace_profile_set_merge_like_comms();\n\t\t\tbreak;\n\t\tcase OPT_tsoffset:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--ts-offset\");\n\t\t\tctx->date2ts = strdup(optarg);\n\t\t\tif (ctx->data_flags & DATA_FL_DATE)\n\t\t\t\tdie(\"Can not use both --date and --ts-offset\");\n\t\t\tctx->data_flags |= DATA_FL_OFFSET;\n\t\t\tbreak;\n\t\tcase OPT_max_graph_depth:\n\t\t\tcheck_instance_die(ctx->instance, \"--max-graph-depth\");\n\t\t\tfree(ctx->instance->max_graph_depth);\n\t\t\tctx->instance->max_graph_depth = strdup(optarg);\n\t\t\tif (!ctx->instance->max_graph_depth)\n\t\t\t\tdie(\"Could not allocate option\");\n\t\t\tbreak;\n\t\tcase OPT_cmdlines_size:\n\t\t\tctx->saved_cmdlines_size = atoi(optarg);\n\t\t\tbreak;\n\t\tcase OPT_no_filter:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--no-filter\");\n\t\t\tno_filter = true;\n\t\t\tbreak;\n\t\tcase OPT_debug:\n\t\t\ttracecmd_set_debug(true);\n\t\t\tbreak;\n\t\tcase OPT_notimeout:\n\t\t\ttracecmd_set_notimeout(true);\n\t\t\tbreak;\n\t\tcase OPT_module:\n\t\t\tcheck_instance_die(ctx->instance, \"--module\");\n\t\t\tif (ctx->instance->filter_mod)\n\t\t\t\tadd_func(&ctx->instance->filter_funcs,\n\t\t\t\t\t ctx->instance->filter_mod, \"*\");\n\t\t\tctx->instance->filter_mod = optarg;\n\t\t\tctx->filtered = 0;\n\t\t\tbreak;\n\t\tcase OPT_tsyncinterval:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--tsync-interval\");\n\t\t\tctx->tsync_loop_interval = atoi(optarg);\n\t\t\tbreak;\n\t\tcase OPT_fork:\n\t\t\tif (!IS_START(ctx))\n\t\t\t\tdie(\"--fork option used for 'start' command only\");\n\t\t\tfork_process = true;\n\t\t\tbreak;\n\t\tcase OPT_daemonize:\n\t\t\tif (!IS_RECORD(ctx))\n\t\t\t\tdie(\"--daemonize option used for 'record' command only\");\n\t\t\tdo_daemonize = true;\n\t\t\tbreak;\n\t\tcase OPT_tsc2nsec:\n\t\t\tret = get_tsc_nsec(&ctx->tsc2nsec.shift,\n\t\t\t\t\t   &ctx->tsc2nsec.mult);\n\t\t\tif (ret)\n\t\t\t\tdie(\"TSC to nanosecond is not supported\");\n\t\t\tctx->instance->flags |= BUFFER_FL_TSC2NSEC;\n\t\t\tbreak;\n\t\tcase OPT_subbuf:\n\t\t\tcheck_instance_die(ctx->instance, \"--subbuf-size\");\n\t\t\tctx->instance->subbuf_size = atoi(optarg);\n\t\t\tbreak;\n\t\tcase OPT_poll:\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--poll\");\n\t\t\trecorder_flags |= TRACECMD_RECORD_POLL;\n\t\t\tbreak;\n\t\tcase OPT_compression:\n\t\t\tcmd_check_die(ctx, CMD_start, *(argv+1), \"--compression\");\n\t\t\tcmd_check_die(ctx, CMD_set, *(argv+1), \"--compression\");\n\t\t\tcmd_check_die(ctx, CMD_stream, *(argv+1), \"--compression\");\n\t\t\tcmd_check_die(ctx, CMD_profile, *(argv+1), \"--compression\");\n\t\t\tif (strcmp(optarg, \"any\") && strcmp(optarg, \"none\") &&\n\t\t\t    !tracecmd_compress_is_supported(optarg, NULL))\n\t\t\t\tdie(\"Compression algorithm  %s is not supported\", optarg);\n\t\t\tctx->compression = strdup(optarg);\n\t\t\tbreak;\n\t\tcase OPT_file_ver:\n\t\t\tif (ctx->curr_cmd != CMD_record && ctx->curr_cmd != CMD_record_agent)\n\t\t\t\tdie(\"--file_version has no effect with the command %s\\n\",\n\t\t\t\t    *(argv+1));\n\t\t\tctx->file_version = atoi(optarg);\n\t\t\tif (ctx->file_version < FILE_VERSION_MIN ||\n\t\t\t    ctx->file_version > FILE_VERSION_MAX)\n\t\t\t\tdie(\"Unsupported file version %d, \"\n\t\t\t\t    \"supported versions are from %d to %d\",\n\t\t\t\t    ctx->file_version, FILE_VERSION_MIN, FILE_VERSION_MAX);\n\t\t\tbreak;\n\t\tcase OPT_quiet:\n\t\tcase 'q':\n\t\t\tquiet = true;\n\t\t\tbreak;\n\t\tcase OPT_verbose:\n\t\t\tif (trace_set_verbose(optarg) < 0)\n\t\t\t\tdie(\"invalid verbose level %s\", optarg);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tremove_instances(del_list);\n\n\t/* If --date is specified, prepend it to all guest VM flags */\n\tif (ctx->date) {\n\t\tstruct buffer_instance *instance;\n\n\t\tfor_all_instances(instance) {\n\t\t\tif (is_guest(instance))\n\t\t\t\tadd_argv(instance, \"--date\", true);\n\t\t}\n\t}\n\n\tif (!ctx->filtered && ctx->instance->filter_mod)\n\t\tadd_func(&ctx->instance->filter_funcs,\n\t\t\t ctx->instance->filter_mod, \"*\");\n\n\tif (do_children && !filter_task && !fpids_count)\n\t\tdie(\" -c can only be used with -F (or -P with event-fork support)\");\n\n\tif ((argc - optind) >= 2) {\n\t\tif (IS_EXTRACT(ctx))\n\t\t\tdie(\"Command extract does not take any commands\\n\"\n\t\t\t    \"Did you mean 'record'?\");\n\t\tctx->run_command = 1;\n\t}\n\tif (ctx->user && !ctx->run_command)\n\t\twarning(\"--user %s is ignored, no command is specified\",\n\t\t\tctx->user);\n\n\tif (top_instance.get_procmap) {\n\t\t /* use ptrace to get procmap on the command exit */\n\t\tif (ctx->run_command) {\n\t\t\tdo_ptrace = 1;\n\t\t} else if (!top_instance.nr_filter_pids) {\n\t\t\twarning(\"--proc-map is ignored for top instance, \"\n\t\t\t\t\"no command or filtered PIDs are specified.\");\n\t\t\ttop_instance.get_procmap = 0;\n\t\t}\n\t}\n\n\tfor_all_instances(instance) {\n\t\tif (instance->get_procmap && !instance->nr_filter_pids) {\n\t\t\twarning(\"--proc-map is ignored for instance %s, \"\n\t\t\t\t\"no filtered PIDs are specified.\",\n\t\t\t\ttracefs_instance_get_name(instance->tracefs));\n\t\t\tinstance->get_procmap = 0;\n\t\t}\n\t}\n}\n\nstatic enum trace_type get_trace_cmd_type(enum trace_cmd cmd)\n{\n\tconst static struct {\n\t\tenum trace_cmd cmd;\n\t\tenum trace_type ttype;\n\t} trace_type_per_command[] = {\n\t\t{CMD_record, TRACE_TYPE_RECORD},\n\t\t{CMD_stream, TRACE_TYPE_STREAM},\n\t\t{CMD_extract, TRACE_TYPE_EXTRACT},\n\t\t{CMD_profile, TRACE_TYPE_STREAM},\n\t\t{CMD_start, TRACE_TYPE_START},\n\t\t{CMD_record_agent, TRACE_TYPE_RECORD},\n\t\t{CMD_set, TRACE_TYPE_SET}\n\t};\n\n\tfor (int i = 0; i < ARRAY_SIZE(trace_type_per_command); i++) {\n\t\tif (trace_type_per_command[i].cmd == cmd)\n\t\t\treturn trace_type_per_command[i].ttype;\n\t}\n\n\tdie(\"Trace type UNKNOWN for the given cmd_fun\");\n}\n\nstatic void finalize_record_trace(struct common_record_context *ctx)\n{\n\tstruct buffer_instance *instance;\n\n\tif (keep)\n\t\treturn;\n\n\tupdate_reset_files();\n\tupdate_reset_triggers();\n\tif (clear_function_filters)\n\t\tclear_func_filters();\n\n\tset_plugin(\"nop\");\n\n\ttracecmd_remove_instances();\n\n\t/* If tracing_on was enabled before we started, set it on now */\n\tfor_all_instances(instance) {\n\t\tif (instance->flags & BUFFER_FL_KEEP)\n\t\t\twrite_tracing_on(instance,\n\t\t\t\t\t instance->tracing_on_init_val);\n\t\tif (is_proxy_server(instance) && instance->network_handle) {\n\t\t\t/* Now wait for the recorder to be ready for us to send more */\n\t\t\ttracecmd_msg_wait(ctx->instance->msg_handle);\n\t\t\tif (ctx->tsc2nsec.mult)\n\t\t\t\tadd_tsc2nsec(ctx->instance->network_handle, &ctx->tsc2nsec);\n\t\t\ttracecmd_write_guest_time_shift(ctx->instance->network_handle,\n\t\t\t\t\t\t\tctx->instance->tsync);\n\t\t\ttracecmd_msg_send_options(ctx->instance->msg_handle,\n\t\t\t\t\t\t  ctx->instance->network_handle);\n\t\t}\n\t\tif (is_agent(instance)) {\n\t\t\ttracecmd_msg_send_close_resp_msg(instance->msg_handle);\n\t\t\ttracecmd_output_close(instance->network_handle);\n\t\t}\n\t}\n\n\tif (host)\n\t\ttracecmd_output_close(ctx->instance->network_handle);\n}\n\nstatic bool has_local_instances(void)\n{\n\tstruct buffer_instance *instance;\n\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance))\n\t\t\tcontinue;\n\t\tif (host && instance->msg_handle)\n\t\t\tcontinue;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void set_tsync_params(struct common_record_context *ctx)\n{\n\tstruct buffer_instance *instance;\n\tint shift, mult;\n\tbool force_tsc = false;\n\tchar *clock = NULL;\n\n\tif (!ctx->clock) {\n\t/*\n\t * If no clock is configured &&\n\t * KVM time sync protocol is available &&\n\t * there is information of each guest PID process &&\n\t * tsc-x86 clock is supported &&\n\t * TSC to nsec multiplier and shift are available:\n\t * force using the x86-tsc clock for this host-guest tracing session\n\t * and store TSC to nsec multiplier and shift.\n\t */\n\t\tif (tcmd_tsync_proto_is_supported(\"kvm\") &&\n\t\t    trace_have_guests_pid() &&\n\t\t    clock_is_supported(NULL, TSC_CLOCK) &&\n\t\t    !get_tsc_nsec(&shift, &mult) && mult) {\n\t\t\tclock = strdup(TSC_CLOCK);\n\t\t\tif (!clock)\n\t\t\t\tdie(\"Cannot not allocate clock\");\n\t\t\tctx->tsc2nsec.mult = mult;\n\t\t\tctx->tsc2nsec.shift = shift;\n\t\t\tforce_tsc = true;\n\t\t} else { /* Use the current clock of the first host instance */\n\t\t\tclock = get_trace_clock(true);\n\t\t}\n\t} else {\n\t\tclock = strdup(ctx->clock);\n\t\tif (!clock)\n\t\t\tdie(\"Cannot not allocate clock\");\n\t}\n\n\tif (!clock && !ctx->tsync_loop_interval)\n\t\tgoto out;\n\tfor_all_instances(instance) {\n\t\tif (clock && !(instance->flags & BUFFER_FL_HAS_CLOCK)) {\n\t\t\t/* use the same clock in all tracing peers */\n\t\t\tif (is_guest(instance)) {\n\t\t\t\tif (!instance->clock) {\n\t\t\t\t\tinstance->clock = strdup(clock);\n\t\t\t\t\tif (!instance->clock)\n\t\t\t\t\t\tdie(\"Can not allocate instance clock\");\n\t\t\t\t}\n\t\t\t\tadd_argv(instance, (char *)instance->clock, true);\n\t\t\t\tadd_argv(instance, \"-C\", true);\n\t\t\t\tif (ctx->tsc2nsec.mult)\n\t\t\t\t\tinstance->flags |= BUFFER_FL_TSC2NSEC;\n\t\t\t} else if (force_tsc && !instance->clock) {\n\t\t\t\tinstance->clock = strdup(clock);\n\t\t\t\tif (!instance->clock)\n\t\t\t\t\tdie(\"Can not allocate instance clock\");\n\t\t\t}\n\t\t}\n\t\tinstance->tsync_loop_interval = ctx->tsync_loop_interval;\n\t}\nout:\n\tfree(clock);\n}\n\nstatic void record_trace(int argc, char **argv,\n\t\t\t struct common_record_context *ctx)\n{\n\tenum trace_type type = get_trace_cmd_type(ctx->curr_cmd);\n\tstruct buffer_instance *instance;\n\tstruct filter_pids *pid;\n\n\tif (do_daemonize)\n\t\tdaemonize_start();\n\n\t/*\n\t * If top_instance doesn't have any plugins or events, then\n\t * remove it from being processed.\n\t */\n\tif (!__check_doing_something(&top_instance) && !filter_task)\n\t\tfirst_instance = buffer_instances;\n\telse\n\t\tctx->topt = 1;\n\n\tupdate_first_instance(ctx->instance, ctx->topt);\n\tif (!Keep) {\n\t\tcheck_doing_something();\n\t\tcheck_function_plugin();\n\t}\n\n\tif (!ctx->output)\n\t\tctx->output = DEFAULT_INPUT_FILE;\n\n\t/* Make sure top_instance.output_file exists */\n\tif (!top_instance.output_file)\n\t\ttop_instance.output_file = strdup(ctx->output);\n\n\tif (ctx->data_flags & (DATA_FL_GUEST | DATA_FL_PROXY))\n\t\tset_tsync_params(ctx);\n\n\tmake_instances();\n\n\t/* Save the state of tracing_on before starting */\n\tfor_all_instances(instance) {\n\t\tif (ctx->temp)\n\t\t\tinstance->temp_dir = ctx->temp;\n\t\t/* The -o could have been done after -B */\n\t\tif (!instance->output_file)\n\t\t\tinstance->output_file = strdup(ctx->output);\n\t\tif (!instance->output_file)\n\t\t\tdie(\"Failed to allocate output file name for instance\");\n\t\tif (!ctx->manual && instance->flags & BUFFER_FL_PROFILE)\n\t\t\tenable_profile(instance);\n\n\t\tinstance->tracing_on_init_val = read_tracing_on(instance);\n\t\t/* Some instances may not be created yet */\n\t\tif (instance->tracing_on_init_val < 0)\n\t\t\tinstance->tracing_on_init_val = 1;\n\t}\n\n\tif (ctx->events)\n\t\texpand_event_list();\n\n\tpage_size = getpagesize();\n\n\tif (!is_guest(ctx->instance))\n\t\tfset = set_ftrace(ctx->instance, !ctx->disable, ctx->total_disable);\n\tif (!Keep)\n\t\ttracecmd_disable_all_tracing(1);\n\n\tfor_all_instances(instance)\n\t\tset_clock(ctx, instance);\n\n\n\t/* Record records the date first */\n\tif (ctx->date &&\n\t    ((IS_RECORD(ctx) && has_local_instances()) || IS_RECORD_AGENT(ctx)))\n\t\tctx->date2ts = get_date_to_ts();\n\n\tfor_all_instances(instance) {\n\t\tset_funcs(instance);\n\t\tset_mask(instance);\n\t}\n\n\tif (ctx->events) {\n\t\tfor_all_instances(instance)\n\t\t\tenable_events(instance);\n\t}\n\n\tset_saved_cmdlines_size(ctx);\n\tset_buffer_size();\n\tupdate_plugins(type);\n\tset_options();\n\n\tfor_all_instances(instance) {\n\t\tif (instance->max_graph_depth) {\n\t\t\tset_max_graph_depth(instance, instance->max_graph_depth);\n\t\t\tfree(instance->max_graph_depth);\n\t\t\tinstance->max_graph_depth = NULL;\n\t\t}\n\t}\n\n\tallocate_seq();\n\n\tif (type & (TRACE_TYPE_RECORD | TRACE_TYPE_STREAM)) {\n\t\tsignal(SIGINT, do_sig);\n\t\tsignal(SIGTERM, do_sig);\n\t\tif (!latency)\n\t\t\tstart_threads(type, ctx);\n\t}\n\n\tif (ctx->run_command) {\n\t\trun_cmd(type, ctx->user, (argc - optind) - 1, &argv[optind + 1]);\n\t} else if (ctx->instance && is_agent(ctx->instance)) {\n\t\tupdate_task_filter();\n\t\ttracecmd_enable_tracing();\n\t\ttracecmd_msg_wait_close(ctx->instance->msg_handle);\n\t} else {\n\t\tbool pwait = false;\n\t\tbool wait_indefinitely = false;\n\n\t\tupdate_task_filter();\n\n\t\tif (!Keep)\n\t\t\ttracecmd_enable_tracing();\n\n\t\tif (type & (TRACE_TYPE_START | TRACE_TYPE_SET))\n\t\t\texit(0);\n\n\t\t/* We don't ptrace ourself */\n\t\tif (do_ptrace) {\n\t\t\tfor_all_instances(instance) {\n\t\t\t\tfor (pid = instance->filter_pids; pid; pid = pid->next) {\n\t\t\t\t\tif (!pid->exclude && instance->ptrace_child) {\n\t\t\t\t\t\tptrace_attach(instance, pid->pid);\n\t\t\t\t\t\tpwait = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (do_daemonize) {\n\t\t\tdaemonize_finish();\n\t\t\tprintf(\"Send SIGINT/SIGTERM to pid %d to stop recording\\n\", getpid());\n\t\t} else {\n\t\t\t/* sleep till we are woken with Ctrl^C */\n\t\t\tprintf(\"Hit Ctrl^C to stop recording\\n\");\n\t\t}\n\n\t\tfor_all_instances(instance) {\n\t\t\t/* If an instance is not tracing individual processes\n\t\t\t * or there is an error while waiting for a process to\n\t\t\t * exit, fallback to waiting indefinitely.\n\t\t\t */\n\t\t\tif (!instance->nr_process_pids ||\n\t\t\t    trace_wait_for_processes(instance))\n\t\t\t\twait_indefinitely = true;\n\t\t}\n\t\twhile (!finished && wait_indefinitely)\n\t\t\ttrace_or_sleep(type, pwait);\n\t\t/* Streams need to be flushed one more time */\n\t\tif (type & TRACE_TYPE_STREAM)\n\t\t\ttrace_stream_read(pids, recorder_threads, -1);\n\t}\n\n\ttell_guests_to_stop(ctx);\n\ttracecmd_disable_tracing();\n\tif (!latency)\n\t\tstop_threads(type);\n\n\trecord_stats();\n\n\tif (!latency)\n\t\twait_threads();\n\n\tif (is_proxy_server(ctx->instance) && ctx->instance->network_handle) {\n\t\ttracecmd_tsync_with_guest_stop(ctx->instance->tsync);\n\t\ttrace_add_guest_info(ctx->instance->network_handle, ctx->instance);\n\t\tif (ctx->tsc2nsec.mult)\n\t\t\tadd_tsc2nsec(ctx->instance->network_handle, &ctx->tsc2nsec);\n\t\ttracecmd_write_options(ctx->instance->network_handle);\n\t\ttracecmd_write_meta_strings(ctx->instance->network_handle);\n\t\ttracecmd_msg_finish_sending_data(ctx->instance->msg_handle);\n\t}\n\n\tif (IS_RECORD(ctx)) {\n\t\trecord_data(ctx);\n\t\tdelete_thread_data();\n\t} else\n\t\tprint_stats();\n\n\tif (!keep)\n\t\ttracecmd_disable_all_tracing(0);\n\n\tdestroy_stats();\n\tfinalize_record_trace(ctx);\n\n\tif (created_pidfile)\n\t\tremove_pid_file(RECORD_PIDFILE);\n}\n\n/*\n * This function contains common code for the following commands:\n * record, start, stream, profile.\n */\nstatic void record_trace_command(int argc, char **argv,\n\t\t\t\t struct common_record_context *ctx)\n{\n\ttracecmd_tsync_init();\n\trecord_trace(argc, argv, ctx);\n}\n\nvoid trace_start(int argc, char **argv)\n{\n\tstruct common_record_context ctx;\n\n\tparse_record_options(argc, argv, CMD_start, &ctx);\n\trecord_trace_command(argc, argv, &ctx);\n\texit(0);\n}\n\nvoid trace_set(int argc, char **argv)\n{\n\tstruct common_record_context ctx;\n\n\t/* Keep the current settings */\n\tKeep = 1;\n\tkeep = 1;\n\tparse_record_options(argc, argv, CMD_set, &ctx);\n\trecord_trace_command(argc, argv, &ctx);\n\texit(0);\n}\n\nstatic void read_last_boot_info(void)\n{\n\tstruct buffer_instance *instance;\n\tchar *path;\n\tchar *buf;\n\n\tfor_all_instances(instance) {\n\t\tif (is_guest(instance))\n\t\t\tcontinue;\n\n\t\tif (!tracefs_file_exists(instance->tracefs, \"last_boot_info\"))\n\t\t\tcontinue;\n\n\t\tpath = tracefs_instance_get_file(instance->tracefs, \"last_boot_info\");\n\t\tif (!path) {\n\t\t\twarning(\"Could not read last_boot_info\");\n\t\t\treturn;\n\t\t}\n\t\tbuf = read_file(path);\n\t\ttracefs_put_tracing_file(path);\n\t\tif (!buf) {\n\t\t\twarning(\"Could not copy last_boot_info\");\n\t\t\treturn;\n\t\t}\n\t\tif (strncmp(buf, \"# Current\", 9) == 0)\n\t\t\tfree(buf);\n\t\telse\n\t\t\tinstance->last_boot_info = buf;\n\t}\n}\n\nvoid trace_extract(int argc, char **argv)\n{\n\tstruct common_record_context ctx;\n\tstruct buffer_instance *instance;\n\tenum trace_type type;\n\n\tparse_record_options(argc, argv, CMD_extract, &ctx);\n\n\ttype = get_trace_cmd_type(ctx.curr_cmd);\n\n\tupdate_first_instance(ctx.instance, ctx.topt);\n\tcheck_function_plugin();\n\n\tif (!ctx.output)\n\t\tctx.output = DEFAULT_INPUT_FILE;\n\n\t/* Save the state of tracing_on before starting */\n\tfor_all_instances(instance) {\n\t\tinstance->output_file = strdup(ctx.output);\n\t\tif (!instance->output_file)\n\t\t\tdie(\"Failed to allocate output file name for instance\");\n\n\t\tif (!ctx.manual && instance->flags & BUFFER_FL_PROFILE)\n\t\t\tenable_profile(ctx.instance);\n\n\t\tinstance->tracing_on_init_val = read_tracing_on(instance);\n\t\t/* Some instances may not be created yet */\n\t\tif (instance->tracing_on_init_val < 0)\n\t\t\tinstance->tracing_on_init_val = 1;\n\n\t\t/* Keep all instances */\n\t\tinstance->flags |= BUFFER_FL_KEEP;\n\t}\n\n\t/* Extracting data records all events in the system. */\n\tif (!ctx.record_all)\n\t\trecord_all_events();\n\n\tif (ctx.events)\n\t\texpand_event_list();\n\n\tread_last_boot_info();\n\n\tpage_size = getpagesize();\n\tupdate_plugins(type);\n\tset_options();\n\n\tfor_all_instances(instance) {\n\t\tif (instance->max_graph_depth) {\n\t\t\tset_max_graph_depth(instance, instance->max_graph_depth);\n\t\t\tfree(instance->max_graph_depth);\n\t\t\tinstance->max_graph_depth = NULL;\n\t\t}\n\t}\n\n\tallocate_seq();\n\tflush_threads();\n\trecord_stats();\n\n\tif (!keep)\n\t\ttracecmd_disable_all_tracing(0);\n\n\t/* extract records the date after extraction */\n\tif (ctx.date) {\n\t\t/*\n\t\t * We need to start tracing, don't let other traces\n\t\t * screw with our trace_marker.\n\t\t */\n\t\ttracecmd_disable_all_tracing(1);\n\t\tctx.date2ts = get_date_to_ts();\n\t}\n\n\trecord_data(&ctx);\n\tdelete_thread_data();\n\tdestroy_stats();\n\tfinalize_record_trace(&ctx);\n\texit(0);\n}\n\nvoid trace_stream(int argc, char **argv)\n{\n\tstruct common_record_context ctx;\n\n\t/* Default sleep time is half a second for streaming */\n\tsleep_time = 500000;\n\n\tparse_record_options(argc, argv, CMD_stream, &ctx);\n\trecord_trace_command(argc, argv, &ctx);\n\texit(0);\n}\n\n\nvoid trace_profile(int argc, char **argv)\n{\n\tstruct common_record_context ctx;\n\n\tparse_record_options(argc, argv, CMD_profile, &ctx);\n\n\thandle_init = trace_init_profile;\n\tctx.events = 1;\n\n\t/*\n\t * If no instances were set, then enable profiling on the top instance.\n\t */\n\tif (!buffer_instances)\n\t\ttop_instance.flags |= BUFFER_FL_PROFILE;\n\n\trecord_trace_command(argc, argv, &ctx);\n\tdo_trace_profile();\n\texit(0);\n}\n\nvoid trace_record(int argc, char **argv)\n{\n\tstruct common_record_context ctx;\n\n\tparse_record_options(argc, argv, CMD_record, &ctx);\n\trecord_trace_command(argc, argv, &ctx);\n\texit(0);\n}\n\n/**\n * trace_record_agent - record command running from the agent\n * @msg_handle: The handle to communicate with the peer\n * @cpus: The number of CPUs the agent has to record\n * @fds: The array of file descriptors for the CPUs\n * @argc: The number of arguments to pass to the record session\n * @argv: The arguments to pass to the record session\n * @use_fifos: True if fifos are used instead of sockets.\n * @trace_id: The agent's trace_id\n * @rcid: Remote cid if the agent is a proxy, negative otherwise.\n * @host: Set if this is an IP connection and not a vsocket one\n *\n * This is used to enable tracing via the record command just\n * like trace-cmd record, but it is being done via the agent\n * and all the data is being transfered to the peer that is\n * connected on the other end of the sockets.\n *\n *  Returns zero on success, negative otherwise.\n */\nint trace_record_agent(struct tracecmd_msg_handle *msg_handle,\n\t\t       int cpus, int *fds,\n\t\t       int argc, char **argv,\n\t\t       bool use_fifos, struct tracecmd_time_sync *tsync,\n\t\t       unsigned long long trace_id, int rcid, const char *host)\n{\n\tstruct common_record_context ctx;\n\tchar **argv_plus;\n\n\t/* Reset optind for getopt_long */\n\toptind = 1;\n\t/*\n\t * argc is the number of elements in argv, but we need to convert\n\t * argc and argv into \"trace-cmd\", \"record\", argv.\n\t * where argc needs to grow by two.\n\t */\n\targv_plus = calloc(argc + 2, sizeof(char *));\n\tif (!argv_plus)\n\t\tdie(\"Failed to allocate record arguments\");\n\n\targv_plus[0] = \"trace-cmd\";\n\targv_plus[1] = \"record\";\n\tmemmove(argv_plus + 2, argv, argc * sizeof(char *));\n\targc += 2;\n\n\tparse_record_options(argc, argv_plus, CMD_record_agent, &ctx);\n\tif (ctx.run_command)\n\t\treturn -EINVAL;\n\n\tctx.instance->fds = fds;\n\tctx.instance->use_fifos = use_fifos;\n\tctx.instance->flags |= BUFFER_FL_AGENT;\n\tif (rcid >= 0)\n\t\tctx.data_flags |= DATA_FL_PROXY;\n\tctx.instance->msg_handle = msg_handle;\n\tctx.instance->host = host;\n\tctx.instance->tsync = tsync;\n\tctx.instance->cid = rcid;\n\tmsg_handle->version = V3_PROTOCOL;\n\ttop_instance.trace_id = trace_id;\n\trecord_trace(argc, argv, &ctx);\n\n\tfree(argv_plus);\n\treturn 0;\n}\n"
  },
  {
    "path": "tracecmd/trace-restore.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <stdarg.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <fcntl.h>\n#include <signal.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <errno.h>\n\n#include \"trace-local.h\"\n\nstatic struct tracecmd_output *create_output(const char *file,\n\t\t\t\t\t     const char *tracing_dir, const char *kallsyms)\n{\n\tstruct tracecmd_output *out;\n\n\tout = tracecmd_output_create(file);\n\tif (!out)\n\t\tgoto error;\n\n\tif (tracing_dir && tracecmd_output_set_trace_dir(out, tracing_dir))\n\t\tgoto error;\n\tif (kallsyms && tracecmd_output_set_kallsyms(out, kallsyms))\n\t\tgoto error;\n\tif (tracecmd_output_write_headers(out, NULL))\n\t\tgoto error;\n\treturn out;\nerror:\n\tif (out)\n\t\ttracecmd_output_close(out);\n\tunlink(file);\n\treturn NULL;\n}\n\nvoid trace_restore (int argc, char **argv)\n{\n\tstruct tracecmd_output *handle;\n\tconst char *output_file = DEFAULT_INPUT_FILE;\n\tconst char *output = NULL;\n\tconst char *input = NULL;\n\tconst char *tracing_dir = NULL;\n\tconst char *kallsyms = NULL;\n\tstruct stat st1;\n\tstruct stat st2;\n\tint first_arg;\n\tint create_only = 0;\n\tint args;\n\tint c;\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"restore\") != 0)\n\t\tusage(argv);\n\n\twhile ((c = getopt(argc-1, argv+1, \"+hco:i:t:k:\")) >= 0) {\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\tif (input)\n\t\t\t\tdie(\"-c and -i are incompatible\");\n\t\t\tcreate_only = 1;\n\t\t\t/* make output default to partial */\n\t\t\toutput_file = \"trace-partial.dat\";\n\t\t\tbreak;\n\n\t\tcase 't':\n\t\t\ttracing_dir = optarg;\n\t\t\tbreak;\n\t\tcase 'k':\n\t\t\tkallsyms = optarg;\n\t\t\tbreak;\n\t\tcase 'o':\n\t\t\tif (output)\n\t\t\t\tdie(\"only one output file allowed\");\n\t\t\toutput = optarg;\n\t\t\tbreak;\n\n\t\tcase 'i':\n\t\t\tif (input)\n\t\t\t\tdie(\"only one input file allowed\");\n\t\t\tif (create_only)\n\t\t\t\tdie(\"-c and -i are incompatible\");\n\t\t\tinput = optarg;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif (!output)\n\t\toutput = output_file;\n\n\tif ((argc - optind) <= 1) {\n\t\tif (!create_only) {\n\t\t\twarning(\"No data files found\");\n\t\t\tusage(argv);\n\t\t}\n\n\t\thandle = create_output(output, tracing_dir, kallsyms);\n\t\tif (!handle)\n\t\t\tdie(\"Unabled to create output file %s\", output);\n\t\tif (tracecmd_write_cmdlines(handle) < 0)\n\t\t\tdie(\"Failed to write command lines\");\n\t\ttracecmd_output_close(handle);\n\t\texit(0);\n\t}\n\tfirst_arg = optind + 1;\n\targs = argc - first_arg;\n\tprintf(\"first = %d %s args=%d\\n\", first_arg, argv[first_arg], args);\n\n\t/* Make sure input and output are not the same file */\n\tif (input && output) {\n\t\tif (stat(input, &st1) < 0)\n\t\t\tdie(\"%s:\", input);\n\t\t/* output exists? otherwise we don't care */\n\t\tif (stat(output, &st2) == 0) {\n\t\t\tif (st1.st_ino == st2.st_ino &&\n\t\t\t    st1.st_dev == st2.st_dev)\n\t\t\t\tdie(\"input and output file are the same\");\n\t\t}\n\t}\n\n\tif (input) {\n\t\tstruct tracecmd_input *ihandle;\n\n\t\tihandle = tracecmd_alloc(input, 0);\n\t\tif (!ihandle)\n\t\t\tdie(\"error reading file %s\", input);\n\t\t/* make sure headers are ok */\n\t\tif (tracecmd_read_headers(ihandle, TRACECMD_FILE_CMD_LINES) < 0)\n\t\t\tdie(\"error reading file %s headers\", input);\n\n\t\thandle = tracecmd_copy(ihandle, output, TRACECMD_FILE_CMD_LINES, 0, NULL);\n\t\ttracecmd_close(ihandle);\n\t} else {\n\t\thandle = tracecmd_output_create(output);\n\t\ttracecmd_output_write_headers(handle, NULL);\n\t}\n\n\tif (!handle)\n\t\tdie(\"error writing to %s\", output);\n\n\tif (tracecmd_append_cpu_data(handle, args, &argv[first_arg]) < 0)\n\t\tdie(\"failed to append data\");\n\n\treturn;\n}\n"
  },
  {
    "path": "tracecmd/trace-setup-guest.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2019 VMware Inc, Slavomir Kaslev <kaslevs@vmware.com>\n *\n */\n\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <grp.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"trace-local.h\"\n#include \"trace-msg.h\"\n\nstatic int make_dir(const char *path, mode_t mode)\n{\n\tchar buf[PATH_MAX+2], *p;\n\n\tstrncpy(buf, path, sizeof(buf));\n\tif (buf[PATH_MAX])\n\t\treturn -E2BIG;\n\n\tfor (p = buf; *p; p++) {\n\t\tp += strspn(p, \"/\");\n\t\tp += strcspn(p, \"/\");\n\t\t*p = '\\0';\n\t\tif (mkdir(buf, mode) < 0 && errno != EEXIST)\n\t\t\treturn -errno;\n\t\t*p = '/';\n\t}\n\n\treturn 0;\n}\n\nstatic int make_fifo(const char *path, mode_t mode)\n{\n\tstruct stat st;\n\n\tif (!stat(path, &st)) {\n\t\tif (S_ISFIFO(st.st_mode))\n\t\t\treturn 0;\n\t\treturn -EEXIST;\n\t}\n\n\tif (mkfifo(path, mode))\n\t\treturn -errno;\n\treturn 0;\n}\n\nstatic int make_guest_dir(const char *guest)\n{\n\tchar path[PATH_MAX];\n\n\tsnprintf(path, sizeof(path), GUEST_DIR_FMT, guest);\n\treturn make_dir(path, 0750);\n}\n\nstatic int make_guest_fifo(const char *guest, int cpu, mode_t mode)\n{\n\tstatic const char *exts[] = {\".in\", \".out\"};\n\tchar path[PATH_MAX];\n\tint i, ret = 0;\n\n\tfor (i = 0; i < ARRAY_SIZE(exts); i++) {\n\t\tsnprintf(path, sizeof(path), GUEST_FIFO_FMT \"%s\",\n\t\t\t guest, cpu, exts[i]);\n\t\tret = make_fifo(path, mode);\n\t\tif (ret < 0)\n\t\t\tbreak;\n\t}\n\n\treturn ret;\n}\n\nstatic int make_guest_fifos(const char *guest, int nr_cpus, mode_t mode)\n{\n\tint i, ret = 0;\n\tmode_t mask;\n\n\tmask = umask(0);\n\tfor (i = 0; i < nr_cpus; i++) {\n\t\tret = make_guest_fifo(guest, i, mode);\n\t\tif (ret < 0)\n\t\t\tbreak;\n\t}\n\tumask(mask);\n\n\treturn ret;\n}\n\nstatic int get_guest_cpu_count(const char *guest)\n{\n\tconst char *cmd_fmt = \"virsh vcpucount --maximum '%s' 2>/dev/null\";\n\tint nr_cpus = -1;\n\tchar cmd[1024];\n\tFILE *f;\n\n\tsnprintf(cmd, sizeof(cmd), cmd_fmt, guest);\n\tf = popen(cmd, \"r\");\n\tif (!f)\n\t\treturn -errno;\n\n\tfscanf(f, \"%d\", &nr_cpus);\n\tpclose(f);\n\n\treturn nr_cpus;\n}\n\nstatic int attach_guest_fifos(const char *guest, int nr_cpus)\n{\n\tconst char *cmd_fmt =\n\t\t\"virsh attach-device --config '%s' '%s' >/dev/null 2>/dev/null\";\n\tconst char *xml_fmt =\n\t\t\"<channel type='pipe'>\\n\"\n\t\t\"  <source path='%s'/>\\n\"\n\t\t\"  <target type='virtio' name='%s%d'/>\\n\"\n\t\t\"</channel>\";\n\tchar tmp_path[PATH_MAX], path[PATH_MAX];\n\tchar cmd[PATH_MAX + 256], xml[PATH_MAX + 256];\n\tint i, fd, ret = 0;\n\n\tstrcpy(tmp_path, \"/tmp/pipexmlXXXXXX\");\n\tfd = mkstemp(tmp_path);\n\tif (fd < 0)\n\t\treturn fd;\n\n\tfor (i = 0; i < nr_cpus; i++) {\n\t\tsnprintf(path, sizeof(path), GUEST_FIFO_FMT, guest, i);\n\t\tsnprintf(xml, sizeof(xml), xml_fmt, path, GUEST_PIPE_NAME, i);\n\t\tpwrite(fd, xml, strlen(xml), 0);\n\n\t\tsnprintf(cmd, sizeof(cmd), cmd_fmt, guest, tmp_path);\n\t\terrno = 0;\n\t\tif (system(cmd) != 0) {\n\t\t\tret = -errno;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tclose(fd);\n\tunlink(tmp_path);\n\n\treturn ret;\n}\n\nstatic void do_setup_guest(const char *guest, int nr_cpus,\n\t\t\t   mode_t mode, gid_t gid, bool attach)\n{\n\tgid_t save_egid;\n\tint ret;\n\n\tif (gid != -1) {\n\t\tsave_egid = getegid();\n\t\tret = setegid(gid);\n\t\tif (ret < 0)\n\t\t\tdie(\"failed to set effective group ID\");\n\t}\n\n\tret = make_guest_dir(guest);\n\tif (ret < 0)\n\t\tdie(\"failed to create guest directory for %s\", guest);\n\n\tret = make_guest_fifos(guest, nr_cpus, mode);\n\tif (ret < 0)\n\t\tdie(\"failed to create FIFOs for %s\", guest);\n\n\tif (attach) {\n\t\tret = attach_guest_fifos(guest, nr_cpus);\n\t\tif (ret < 0)\n\t\t\tdie(\"failed to attach FIFOs to %s\", guest);\n\t}\n\n\tif (gid != -1) {\n\t\tret = setegid(save_egid);\n\t\tif (ret < 0)\n\t\t\tdie(\"failed to restore effective group ID\");\n\t}\n}\n\nvoid trace_setup_guest(int argc, char **argv)\n{\n\tbool attach = false;\n\tstruct group *group;\n\tmode_t mode = 0660;\n\tint nr_cpus = -1;\n\tgid_t gid = -1;\n\tchar *guest;\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"setup-guest\") != 0)\n\t\tusage(argv);\n\n\tfor (;;) {\n\t\tint c, option_index = 0;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long(argc-1, argv+1, \"+hc:p:g:a\",\n\t\t\t\tlong_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\tnr_cpus = atoi(optarg);\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tmode = strtol(optarg, NULL, 8);\n\t\t\tbreak;\n\t\tcase 'g':\n\t\t\tgroup = getgrnam(optarg);\n\t\t\tif (!group)\n\t\t\t\tdie(\"group %s does not exist\", optarg);\n\t\t\tgid = group->gr_gid;\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tattach = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif (optind != argc-2)\n\t\tusage(argv);\n\n\tguest = argv[optind+1];\n\n\tif (nr_cpus <= 0)\n\t\tnr_cpus = get_guest_cpu_count(guest);\n\n\tif (nr_cpus <= 0)\n\t\tdie(\"invalid number of cpus for guest %s\", guest);\n\n\tdo_setup_guest(guest, nr_cpus, mode, gid, attach);\n}\n"
  },
  {
    "path": "tracecmd/trace-show.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdlib.h>\n#include <getopt.h>\n#include <errno.h>\n\n#include \"tracefs.h\"\n#include \"trace-local.h\"\n\nenum {\n\tOPT_cpumask\t\t\t= 240,\n\tOPT_graph_notrace,\n\tOPT_graph_function,\n\tOPT_ftrace_pid,\n\tOPT_ftrace_notrace,\n\tOPT_ftrace_filter,\n\tOPT_buffer_subbuf_size_kb,\n\tOPT_buffer_total_size_kb,\n\tOPT_buffer_size_kb,\n\tOPT_buffer_percent,\n\tOPT_current_tracer,\n\tOPT_tracing_on,\n\tOPT_hist,\n\tOPT_trigger,\n\tOPT_max_latency,\n};\n\nvoid trace_show(int argc, char **argv)\n{\n\tconst char *buffer = NULL;\n\tconst char *file = \"trace\";\n\tconst char *cpu = NULL;\n\tstruct buffer_instance *instance = &top_instance;\n\tchar *hist = NULL;\n\tchar *trigger = NULL;\n\tchar cpu_path[128];\n\tchar *path;\n\tint snap = 0;\n\tint pipe = 0;\n\tint show_name = 0;\n\tint option_index = 0;\n\tint stop = 0;\n\tint c;\n\tstatic struct option long_options[] = {\n\t\t{\"hist\", required_argument, NULL, OPT_hist},\n\t\t{\"trigger\", required_argument, NULL, OPT_trigger},\n\t\t{\"tracing_on\", no_argument, NULL, OPT_tracing_on},\n\t\t{\"current_tracer\", no_argument, NULL, OPT_current_tracer},\n\t\t{\"buffer_size\", no_argument, NULL, OPT_buffer_size_kb},\n\t\t{\"buffer_total_size\", no_argument, NULL, OPT_buffer_total_size_kb},\n\t\t{\"buffer_subbuf_size\", no_argument, NULL, OPT_buffer_subbuf_size_kb},\n\t\t{\"buffer_percent\", no_argument, NULL, OPT_buffer_percent},\n\t\t{\"ftrace_filter\", no_argument, NULL, OPT_ftrace_filter},\n\t\t{\"ftrace_notrace\", no_argument, NULL, OPT_ftrace_notrace},\n\t\t{\"ftrace_pid\", no_argument, NULL, OPT_ftrace_pid},\n\t\t{\"graph_function\", no_argument, NULL, OPT_graph_function},\n\t\t{\"graph_notrace\", no_argument, NULL, OPT_graph_notrace},\n\t\t{\"cpumask\", no_argument, NULL, OPT_cpumask},\n\t\t{\"max_latency\", no_argument, NULL, OPT_max_latency},\n\t\t{\"help\", no_argument, NULL, '?'},\n\t\t{NULL, 0, NULL, 0}\n\t};\n\n\tinit_top_instance();\n\n\twhile ((c = getopt_long(argc-1, argv+1, \"B:c:fsp\",\n\t\t\t\tlong_options, &option_index)) >= 0) {\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tif (buffer)\n\t\t\t\tdie(\"Can only show one buffer at a time\");\n\t\t\tbuffer = optarg;\n\t\t\tinstance = allocate_instance(optarg);\n\t\t\tif (!instance)\n\t\t\t\tdie(\"Failed to create instance\");\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\tif (cpu)\n\t\t\t\tdie(\"Can only show one CPU at a time\");\n\t\t\tcpu = optarg;\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tshow_name = 1;\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tsnap = 1;\n\t\t\tif (pipe)\n\t\t\t\tdie(\"Can not have -s and -p together\");\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tpipe = 1;\n\t\t\tif (snap)\n\t\t\t\tdie(\"Can not have -s and -p together\");\n\t\t\tbreak;\n\t\tcase OPT_hist:\n\t\t\thist = optarg;\n\t\t\tbreak;\n\t\tcase OPT_trigger:\n\t\t\ttrigger = optarg;\n\t\t\tbreak;\n\n\t\tcase OPT_tracing_on:\n\t\t\tshow_instance_file(instance, \"tracing_on\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_current_tracer:\n\t\t\tshow_instance_file(instance, \"current_tracer\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_buffer_size_kb:\n\t\t\tshow_instance_file(instance, \"buffer_size_kb\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_buffer_total_size_kb:\n\t\t\tshow_instance_file(instance, \"buffer_total_size_kb\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_buffer_subbuf_size_kb:\n\t\t\tshow_instance_file(instance, \"buffer_subbuf_size_kb\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_buffer_percent:\n\t\t\tshow_instance_file(instance, \"buffer_percent\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_ftrace_filter:\n\t\t\tshow_instance_file(instance, \"set_ftrace_filter\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_ftrace_notrace:\n\t\t\tshow_instance_file(instance, \"set_ftrace_notrace\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_ftrace_pid:\n\t\t\tshow_instance_file(instance, \"set_ftrace_pid\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_graph_function:\n\t\t\tshow_instance_file(instance, \"set_graph_function\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_graph_notrace:\n\t\t\tshow_instance_file(instance, \"set_graph_notrace\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_cpumask:\n\t\t\tshow_instance_file(instance, \"tracing_cpumask\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tcase OPT_max_latency:\n\t\t\tshow_instance_file(instance, \"tracing_max_latency\");\n\t\t\tstop = 1;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\tif (stop)\n\t\texit(0);\n\tif (pipe)\n\t\tfile = \"trace_pipe\";\n\telse if (snap)\n\t\tfile = \"snapshot\";\n\n\tif (hist || trigger) {\n\t\tchar **systems = NULL;\n\t\tchar *system = NULL;\n\t\tchar *event = hist ? hist : trigger;\n\t\tchar *file = hist ? \"hist\" : \"trigger\";\n\t\tchar *p;\n\n\t\tif ((p = strstr(event, \":\"))) {\n\t\t\tsystem = event;\n\t\t\tevent = p + 1;\n\t\t\t*p = '\\0';\n\t\t}\n\n\t\tif (!system) {\n\t\t\tsystems = tracefs_event_systems(NULL);\n\n\t\t\tfor (int i = 0; systems && systems[i]; i++) {\n\t\t\t\tsystem = systems[i];\n\t\t\t\tif (tracefs_event_file_exists(instance->tracefs,\n\t\t\t\t\t\t\t      system, event, file))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!system)\n\t\t\t\tdie(\"Could not find system of event %s\",\n\t\t\t\t    event);\n\t\t}\n\n\t\tpath = tracefs_event_file_read(instance->tracefs,\n\t\t\t\t\t       system, event, file, NULL);\n\t\ttracefs_list_free(systems);\n\t\tif (!path)\n\t\t\tdie(\"Could not find hist for %s%s%s\",\n\t\t\t    system ? system : \"\", system ? \":\":\"\", event);\n\t\tprintf(\"%s\\n\", path);\n\t\tfree(path);\n\t\texit(0);\n\t}\n\n\tif (cpu) {\n\t\tchar *endptr;\n\t\tlong val;\n\n\t\terrno = 0;\n\t\tval = strtol(cpu, &endptr, 0);\n\t\tif (errno || cpu == endptr)\n\t\t\tdie(\"Invalid CPU index '%s'\", cpu);\n\t\tsnprintf(cpu_path, 128, \"per_cpu/cpu%ld/%s\", val, file);\n\t\tfile = cpu_path;\n\t}\n\n\tif (buffer) {\n\t\tint ret;\n\n\t\tret = asprintf(&path, \"instances/%s/%s\", buffer, file);\n\t\tif (ret < 0)\n\t\t\tdie(\"Failed to allocate instance path %s\", file);\n\t\tfile = path;\n\t}\n\n\tif (show_name) {\n\t\tchar *name;\n\t\tname = tracefs_get_tracing_file(file);\n\t\tprintf(\"%s\\n\", name);\n\t\ttracefs_put_tracing_file(name);\n\t}\n\tshow_file(file);\n\tif (buffer)\n\t\tfree(path);\n\n\treturn;\n}\n"
  },
  {
    "path": "tracecmd/trace-snapshot.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2013 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n#include \"tracefs.h\"\n#include \"trace-local.h\"\n\nstatic void write_file(const char *name, char *val)\n{\n\tchar *path;\n\tint fd;\n\tssize_t n;\n\n\tpath = tracefs_get_tracing_file(name);\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0)\n\t\tdie(\"writing %s\", path);\n\n\tn = write(fd, val, strlen(val));\n\tif (n < 0)\n\t\tdie(\"failed to write to %s\\n\", path);\n\n\ttracefs_put_tracing_file(path);\n\tclose(fd);\n}\n\nvoid trace_snapshot (int argc, char **argv)\n{\n\tconst char *buffer = NULL;\n\tconst char *file = \"snapshot\";\n\tstruct stat st;\n\tchar *name;\n\tchar cpu_path[128];\n\tint take_snap = 0;\n\tint reset_snap = 0;\n\tint free_snap = 0;\n\tint cpu = -1;\n\tint ret;\n\tint c;\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"snapshot\") != 0)\n\t\tusage(argv);\n\n\twhile ((c = getopt(argc-1, argv+1, \"srfB:c:\")) >= 0) {\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\ttake_snap = 1;\n\t\t\tif (free_snap)\n\t\t\t\tdie(\"can't take snapshot and free it at the same time\");\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tfree_snap = 1;\n\t\t\tif (take_snap)\n\t\t\t\tdie(\"can't take snapshot and free it at the same time\");\n\t\t\tbreak;\n\t\tcase 'r':\n\t\t\treset_snap = 1;\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tif (buffer)\n\t\t\t\tdie(\"Can only do one buffer at a time\");\n\t\t\tbuffer = optarg;\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\tif (cpu >= 0)\n\t\t\t\tdie(\"Can only do one CPU (or all) at a time\");\n\t\t\tcpu = atoi(optarg);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif (cpu >= 0) {\n\t\tsnprintf(cpu_path, 128, \"per_cpu/cpu%d/%s\", cpu, file);\n\t\tfile = cpu_path;\n\t}\n\n\tname = tracefs_get_tracing_file(file);\n\tret = stat(name, &st);\n\tif (ret < 0)\n\t\tdie(\"Snapshot feature is not supported by this kernel\");\n\ttracefs_put_tracing_file(name);\n\n\tif (!reset_snap && !take_snap && !free_snap) {\n\t\tshow_file(file);\n\t\texit(0);\n\t}\n\n\tif (reset_snap)\n\t\twrite_file(file, \"2\");\n\n\tif (free_snap)\n\t\twrite_file(file, \"0\");\n\n\tif (take_snap)\n\t\twrite_file(file, \"1\");\n}\n"
  },
  {
    "path": "tracecmd/trace-split.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <libgen.h>\n#include <getopt.h>\n#include <stdarg.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <errno.h>\n\n#include \"list.h\"\n#include \"trace-local.h\"\n\nstatic unsigned int page_size;\nstatic const char *default_input_file = DEFAULT_INPUT_FILE;\nstatic const char *default_top_instance_name = \"top\";\nstatic const char *input_file;\n\nenum split_types {\n\tSPLIT_NONE,\n\t/* The order of these must be reverse of the case statement in the options */\n\tSPLIT_SECONDS,\n\tSPLIT_MSECS,\n\tSPLIT_USECS,\n\tSPLIT_EVENTS,\n\tSPLIT_PAGES,\n\tSPLIT_NR_TYPES,\n};\n\nstruct cpu_data {\n\tunsigned long long\t\tts;\n\tunsigned long long\t\toffset;\n\tunsigned long long\t\tmissed_events;\n\tstruct tep_record\t\t*record;\n\tint\t\t\t\tcpu;\n\tint\t\t\t\tfd;\n\tint\t\t\t\tindex;\n\tvoid\t\t\t\t*commit;\n\tvoid\t\t\t\t*page;\n\tchar\t\t\t\t*file;\n};\n\nstruct handle_list {\n\tstruct list_head\t\tlist;\n\tchar\t\t\t\t*name;\n\tint\t\t\t\tindex;\n\tstruct tracecmd_input\t\t*handle;\n\n\t/* Identify the top instance in the input trace. */\n\tbool\t\t\t\twas_top_instance;\n};\n\nstatic struct list_head handle_list;\n\n/**\n * get_handle - Obtain a handle that must be closed once finished.\n */\nstatic struct tracecmd_input *get_handle(struct handle_list *item)\n{\n\tstruct tracecmd_input *top_handle, *handle;\n\n\ttop_handle = tracecmd_open(input_file, 0);\n\tif (!top_handle)\n\t\tdie(\"Error reading %s\", input_file);\n\n\tif (item->was_top_instance) {\n\t\treturn top_handle;\n\t} else {\n\t\thandle = tracecmd_buffer_instance_handle(top_handle, item->index);\n\t\tif (!handle)\n\t\t\twarning(\"Could not retrieve handle %s\", item->name);\n\n\t\ttracecmd_close(top_handle);\n\t\treturn handle;\n\t}\n}\n\nstatic void add_handle(const char *name, int index, bool was_top_instance)\n{\n\tstruct handle_list *item;\n\n\titem = calloc(1, sizeof(*item));\n\tif (!item)\n\t\tdie(\"Failed to allocate handle item\");\n\n\titem->name = strdup(name);\n\tif (!item->name)\n\t\tdie(\"Failed to duplicate %s\", name);\n\n\titem->index = index;\n\titem->was_top_instance = was_top_instance;\n\titem->handle = get_handle(item);\n\tlist_add_tail(&item->list, &handle_list);\n}\n\nstatic void free_handles(struct list_head *list)\n{\n\tstruct handle_list *item, *n;\n\n\tlist_for_each_entry_safe(item, n, list, list) {\n\t\tlist_del(&item->list);\n\t\tfree(item->name);\n\t\ttracecmd_close(item->handle);\n\t\tfree(item);\n\t}\n}\n\nstatic struct list_head inst_list;\n\nstruct inst_list {\n\tstruct list_head\t\tlist;\n\tchar\t\t\t\t*name;\n\tstruct handle_list\t\t*handle;\n\n\t/* Identify the top instance in the input trace. */\n\tbool\t\t\t\twas_top_instance;\n\n\t/* Identify the top instance in the output trace. */\n\tbool\t\t\t\tis_top_instance;\n};\n\nstatic void free_inst(struct list_head *list)\n{\n\tstruct inst_list *item, *n;\n\n\tlist_for_each_entry_safe(item, n, list, list) {\n\t\tlist_del(&item->list);\n\t\tfree(item->name);\n\t\tfree(item);\n\t}\n}\n\nstatic struct inst_list *add_inst(const char *name, bool was_top_instance,\n\t\t\t\t  bool is_top_instance)\n{\n\tstruct inst_list *item;\n\n\titem = calloc(1, sizeof(*item));\n\tif (!item)\n\t\tdie(\"Failed to allocate output_file item\");\n\n\titem->name = strdup(name);\n\tif (!item->name)\n\t\tdie(\"Failed to duplicate %s\", name);\n\n\titem->was_top_instance = was_top_instance;\n\titem->is_top_instance = is_top_instance;\n\tlist_add_tail(&item->list, &inst_list);\n\treturn item;\n}\n\nstatic int create_type_len(struct tep_handle *pevent, int time, int len)\n{\n\tstatic int bigendian = -1;\n\tchar *ptr;\n\tint test;\n\n\tif (bigendian < 0) {\n\t\ttest = 0x4321;\n\t\tptr = (char *)&test;\n\t\tif (*ptr == 0x21)\n\t\t\tbigendian = 0;\n\t\telse\n\t\t\tbigendian = 1;\n\t}\n\n\tif (tep_is_file_bigendian(pevent))\n\t\ttime |= (len << 27);\n\telse\n\t\ttime = (time << 5) | len;\n\n\treturn tep_read_number(pevent, &time, 4);\n}\n\nstatic int write_record(struct tracecmd_input *handle,\n\t\t\tstruct tep_record *record,\n\t\t\tstruct cpu_data *cpu_data,\n\t\t\tenum split_types type)\n{\n\tunsigned long long diff;\n\tstruct tep_handle *pevent;\n\tvoid *page;\n\tint len = 0;\n\tchar *ptr;\n\tint index = 0;\n\tint time;\n\n\tpage = cpu_data->page;\n\n\tpevent = tracecmd_get_tep(handle);\n\n\tptr = page + cpu_data->index;\n\n\tdiff = record->ts - cpu_data->ts;\n\tif (diff > (1 << 27)) {\n\t\t/* Add a time stamp */\n\t\tlen = RINGBUF_TYPE_TIME_EXTEND;\n\t\ttime = (unsigned int)(diff & ((1ULL << 27) - 1));\n\t\ttime = create_type_len(pevent, time, len);\n\t\t*(unsigned *)ptr = time;\n\t\tptr += 4;\n\t\ttime = (unsigned int)(diff >> 27);\n\t\t*(unsigned *)ptr = tep_read_number(pevent, &time, 4);\n\t\tcpu_data->ts = record->ts;\n\t\tcpu_data->index += 8;\n\t\treturn 0;\n\t}\n\n\tif (record->size && (record->size <= 28 * 4))\n\t\tlen = record->size / 4;\n\n\ttime = (unsigned)diff;\n\ttime = create_type_len(pevent, time, len);\n\n\tmemcpy(ptr, &time, 4);\n\tptr += 4;\n\tindex = 4;\n\n\tif (!len) {\n\t\tlen = record->size + 4;\n\t\tif ((len + 4) > record->record_size)\n\t\t\tdie(\"Bad calculation of record len (expect:%d actual:%d)\",\n\t\t\t    record->record_size, len + 4);\n\t\t*(unsigned *)ptr = tep_read_number(pevent, &len, 4);\n\t\tptr += 4;\n\t\tindex += 4;\n\t}\n\n\tlen = (record->size + 3) & ~3;\n\tindex += len;\n\n\tmemcpy(ptr, record->data, len);\n\n\tcpu_data->index += index;\n\tcpu_data->ts = record->ts;\n\n\treturn 1;\n}\n\n#define MISSING_EVENTS (1UL << 31)\n#define MISSING_STORED (1UL << 30)\n\n#define COMMIT_MASK ((1 << 27) - 1)\n\nstatic void write_page(struct tep_handle *pevent,\n\t\t       struct cpu_data *cpu_data, int long_size)\n{\n\tunsigned long long *ptr = NULL;\n\tunsigned int flags = 0;\n\n\tif (cpu_data->missed_events) {\n\t\tflags |= MISSING_EVENTS;\n\t\tif (cpu_data->missed_events > 0) {\n\t\t\tflags |= MISSING_STORED;\n\t\t\tptr = cpu_data->page + cpu_data->index;\n\t\t}\n\t}\n\n\tif (long_size == 8) {\n\t\tunsigned long long index = cpu_data->index - 16 + flags;;\n\t\t*(unsigned long long *)cpu_data->commit =\n\t\t\t\ttep_read_number(pevent, &index, 8);\n\t} else {\n\t\tunsigned int index = cpu_data->index - 12 + flags;;\n\t\t*(unsigned int *)cpu_data->commit =\n\t\t\ttep_read_number(pevent, &index, 4);\n\t}\n\tif (ptr)\n\t\t*ptr = tep_read_number(pevent, &cpu_data->missed_events, 8);\n\n\twrite(cpu_data->fd, cpu_data->page, page_size);\n}\n\nstatic struct tep_record *read_record(struct tracecmd_input *handle,\n\t\t\t\t      int percpu, int *cpu)\n{\n\tif (percpu)\n\t\treturn tracecmd_read_data(handle, *cpu);\n\n\treturn tracecmd_read_next_data(handle, cpu);\n}\n\nstatic void set_cpu_time(struct tracecmd_input *handle,\n\t\t\t int percpu, unsigned long long start, int cpu, int cpus)\n{\n\tif (percpu) {\n\t\ttracecmd_set_cpu_to_timestamp(handle, cpu, start);\n\t\treturn;\n\t}\n\n\tfor (cpu = 0; cpu < cpus; cpu++)\n\t\ttracecmd_set_cpu_to_timestamp(handle, cpu, start);\n\treturn;\n}\n\nstatic int parse_cpu(struct tracecmd_input *handle,\n\t\t     struct cpu_data *cpu_data,\n\t\t     unsigned long long start,\n\t\t     unsigned long long end,\n\t\t     int count_limit, int percpu, int cpu,\n\t\t     enum split_types type, bool *end_reached)\n{\n\tstruct tep_record *record;\n\tstruct tep_handle *pevent;\n\tvoid *ptr;\n\tint page_size;\n\tint long_size = 0;\n\tint cpus;\n\tint count = 0;\n\tint pages = 0;\n\n\tcpus = tracecmd_cpus(handle);\n\n\tlong_size = tracecmd_long_size(handle);\n\tpage_size = tracecmd_page_size(handle);\n\tpevent = tracecmd_get_tep(handle);\n\n\t/* Force new creation of first page */\n\tif (percpu) {\n\t\tcpu_data[cpu].index = page_size + 1;\n\t\tcpu_data[cpu].page = NULL;\n\t} else {\n\t\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\t\tcpu_data[cpu].index = page_size + 1;\n\t\t\tcpu_data[cpu].page = NULL;\n\t\t}\n\t}\n\n\t/*\n\t * Get the cpu pointers up to the start of the\n\t * start time stamp.\n\t */\n\n\trecord = read_record(handle, percpu, &cpu);\n\n\tif (start) {\n\t\tset_cpu_time(handle, percpu, start, cpu, cpus);\n\t\twhile (record && record->ts < start) {\n\t\t\ttracecmd_free_record(record);\n\t\t\trecord = read_record(handle, percpu, &cpu);\n\t\t}\n\t} else if (record)\n\t\tstart = record->ts;\n\n\twhile (record && (!end || record->ts <= end)) {\n\t\tif ((cpu_data[cpu].index + record->record_size > page_size) ||\n\t\t    record->missed_events) {\n\n\t\t\tif (type == SPLIT_PAGES && ++pages > count_limit)\n\t\t\t\tbreak;\n\n\t\t\tif (cpu_data[cpu].page)\n\t\t\t\twrite_page(pevent, &cpu_data[cpu], long_size);\n\t\t\telse {\n\t\t\t\tcpu_data[cpu].page = malloc(page_size);\n\t\t\t\tif (!cpu_data[cpu].page)\n\t\t\t\t\tdie(\"Failed to allocate page\");\n\t\t\t}\n\n\t\t\tcpu_data[cpu].missed_events = record->missed_events;\n\n\t\t\tmemset(cpu_data[cpu].page, 0, page_size);\n\t\t\tptr = cpu_data[cpu].page;\n\n\t\t\t*(unsigned long long*)ptr =\n\t\t\t\ttep_read_number(pevent, &(record->ts), 8);\n\t\t\tcpu_data[cpu].ts = record->ts;\n\t\t\tptr += 8;\n\t\t\tcpu_data[cpu].commit = ptr;\n\t\t\tptr += long_size;\n\t\t\tcpu_data[cpu].index = 8 + long_size;\n\t\t}\n\n\t\tcpu_data[cpu].offset = record->offset;\n\n\t\tif (write_record(handle, record, &cpu_data[cpu], type)) {\n\t\t\ttracecmd_free_record(record);\n\t\t\trecord = read_record(handle, percpu, &cpu);\n\n\t\t\t/* if we hit the end of the cpu, clear the offset */\n\t\t\tif (!record) {\n\t\t\t\tif (percpu)\n\t\t\t\t\tcpu_data[cpu].offset = 0;\n\t\t\t\telse\n\t\t\t\t\tfor (cpu = 0; cpu < cpus; cpu++)\n\t\t\t\t\t\tcpu_data[cpu].offset = 0;\n\t\t\t}\n\n\t\t\tswitch (type) {\n\t\t\tcase SPLIT_NONE:\n\t\t\t\tbreak;\n\t\t\tcase SPLIT_SECONDS:\n\t\t\t\tif (record &&\n\t\t\t\t    record->ts >\n\t\t\t\t    (start + (unsigned long long)count_limit * 1000000000ULL)) {\n\t\t\t\t\ttracecmd_free_record(record);\n\t\t\t\t\trecord = NULL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase SPLIT_MSECS:\n\t\t\t\tif (record &&\n\t\t\t\t    record->ts >\n\t\t\t\t    (start + (unsigned long long)count_limit * 1000000ULL)) {\n\t\t\t\t\ttracecmd_free_record(record);\n\t\t\t\t\trecord = NULL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase SPLIT_USECS:\n\t\t\t\tif (record &&\n\t\t\t\t    record->ts >\n\t\t\t\t    (start + (unsigned long long)count_limit * 1000ULL)) {\n\t\t\t\t\ttracecmd_free_record(record);\n\t\t\t\t\trecord = NULL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase SPLIT_EVENTS:\n\t\t\t\tif (++count >= count_limit) {\n\t\t\t\t\ttracecmd_free_record(record);\n\t\t\t\t\trecord = NULL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (record && (record->ts > end))\n\t\t*end_reached = true;\n\telse\n\t\t*end_reached = false;\n\n\tif (record)\n\t\ttracecmd_free_record(record);\n\n\tif (percpu) {\n\t\tif (cpu_data[cpu].page) {\n\t\t\twrite_page(pevent, &cpu_data[cpu], long_size);\n\t\t\tfree(cpu_data[cpu].page);\n\t\t\tcpu_data[cpu].page = NULL;\n\t\t}\n\t} else {\n\t\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\t\tif (cpu_data[cpu].page) {\n\t\t\t\twrite_page(pevent, &cpu_data[cpu], long_size);\n\t\t\t\tfree(cpu_data[cpu].page);\n\t\t\t\tcpu_data[cpu].page = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nstatic char *get_temp_file(const char *output_file, const char *name, int cpu)\n{\n\tconst char *dot;\n\tchar *file = NULL;\n\tchar *output;\n\tchar *base;\n\tchar *dir;\n\tint ret;\n\n\tif (name)\n\t\tdot = \".\";\n\telse\n\t\tdot = name = \"\";\n\n\toutput = strdup(output_file);\n\tif (!output)\n\t\tdie(\"Failed to duplicate %s\", output_file);\n\n\t/* Extract basename() first, as dirname() truncates output */\n\tbase = basename(output);\n\tdir = dirname(output);\n\n\tret = asprintf(&file, \"%s/.tmp.%s.%s%s%d\", dir, base, name, dot, cpu);\n\tif (ret < 0)\n\t\tdie(\"Failed to allocate file for %s %s %s %d\", dir, base, name, cpu);\n\tfree(output);\n\treturn file;\n}\n\nstatic void delete_temp_file(const char *name)\n{\n\tunlink(name);\n}\n\nstatic void put_temp_file(char *file)\n{\n\tfree(file);\n}\n\nstatic void touch_file(const char *file)\n{\n\tint fd;\n\n\tfd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);\n\tif (fd < 0)\n\t\tdie(\"could not create file %s\\n\", file);\n\tclose(fd);\n}\n\nstatic unsigned long long parse_file(struct tracecmd_input *handle,\n\t\t\t\t     const char *output_file,\n\t\t\t\t     unsigned long long start,\n\t\t\t\t     unsigned long long end, int percpu,\n\t\t\t\t     int only_cpu, int count,\n\t\t\t\t     enum split_types type,\n\t\t\t\t     bool *end_reached)\n{\n\tunsigned long long current = 0;\n\tstruct tracecmd_output *ohandle;\n\tstruct inst_list *inst_entry;\n\tstruct cpu_data *cpu_data;\n\tstruct tep_record *record;\n\tbool all_end_reached = true;\n\tchar **cpu_list;\n\tchar *file;\n\tint cpus;\n\tint cpu;\n\tint ret;\n\tint fd;\n\n\tohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL);\n\ttracecmd_set_out_clock(ohandle, tracecmd_get_trace_clock(handle));\n\n\tlist_for_each_entry(inst_entry, &inst_list, list) {\n\t\tstruct tracecmd_input *curr_handle;\n\t\tbool curr_end_reached = false;\n\n\t\tcurr_handle = inst_entry->handle->handle;\n\t\tcpus = tracecmd_cpus(curr_handle);\n\t\tcpu_data = malloc(sizeof(*cpu_data) * cpus);\n\t\tif (!cpu_data)\n\t\t\tdie(\"Failed to allocate cpu_data for %d cpus\", cpus);\n\n\t\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\t\tfile = get_temp_file(output_file, inst_entry->name, cpu);\n\t\t\ttouch_file(file);\n\n\t\t\tfd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);\n\t\t\tcpu_data[cpu].cpu = cpu;\n\t\t\tcpu_data[cpu].fd = fd;\n\t\t\tcpu_data[cpu].file = file;\n\t\t\tcpu_data[cpu].offset = 0;\n\t\t\tif (start)\n\t\t\t\ttracecmd_set_cpu_to_timestamp(curr_handle, cpu, start);\n\t\t}\n\n\t\tif (only_cpu >= 0) {\n\t\t\tparse_cpu(curr_handle, cpu_data, start, end, count,\n\t\t\t\t  1, only_cpu, type, &curr_end_reached);\n\t\t} else if (percpu) {\n\t\t\tfor (cpu = 0; cpu < cpus; cpu++)\n\t\t\t\tparse_cpu(curr_handle, cpu_data, start,\n\t\t\t\t\t  end, count, percpu, cpu, type, &curr_end_reached);\n\t\t} else {\n\t\t\tparse_cpu(curr_handle, cpu_data, start,\n\t\t\t\t  end, count, percpu, -1, type, &curr_end_reached);\n\t\t}\n\n\t\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\t\tclose(cpu_data[cpu].fd);\n\t\t\tcpu_data[cpu].fd = -1;\n\t\t}\n\n\t\t/* End is reached when all instances finished. */\n\t\tall_end_reached &= curr_end_reached;\n\n\t\tcpu_list = malloc(sizeof(*cpu_list) * cpus);\n\t\tif (!cpu_list)\n\t\t\tdie(\"Failed to allocate cpu_list for %d cpus\", cpus);\n\t\tfor (cpu = 0; cpu < cpus; cpu++)\n\t\t\tcpu_list[cpu] = cpu_data[cpu].file;\n\n\t\tif (inst_entry->is_top_instance)\n\t\t\tret = tracecmd_append_cpu_data(ohandle, cpus, cpu_list);\n\t\telse\n\t\t\tret = tracecmd_append_buffer_cpu_data(ohandle, inst_entry->name, cpus,\n\t\t\t\t\t\t\t      cpu_list);\n\t\tif (ret < 0)\n\t\t\tdie(\"Failed to append tracing data\\n\");\n\n\t\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\t\t/* Set the tracecmd cursor to the next set of records */\n\t\t\tif (cpu_data[cpu].offset) {\n\t\t\t\trecord = tracecmd_read_at(curr_handle, cpu_data[cpu].offset, NULL);\n\t\t\t\tif (record && (!current || record->ts > current))\n\t\t\t\t\tcurrent = record->ts + 1;\n\t\t\t\ttracecmd_free_record(record);\n\t\t\t}\n\t\t}\n\n\t\tfor (cpu = 0; cpu < cpus; cpu++) {\n\t\t\tdelete_temp_file(cpu_data[cpu].file);\n\t\t\tput_temp_file(cpu_data[cpu].file);\n\t\t}\n\t\tfree(cpu_data);\n\t\tfree(cpu_list);\n\t}\n\n\ttracecmd_output_close(ohandle);\n\n\t*end_reached = all_end_reached;\n\treturn current;\n}\n\n/* Map the instance names to their handle. */\nstatic void map_inst_handle(void)\n{\n\tstruct handle_list *handle_entry;\n\tstruct inst_list *inst_entry;\n\n\t/*\n\t * No specific instance was given for this output file.\n\t * Add all the available instances.\n\t */\n\tif (list_empty(&inst_list)) {\n\t\tlist_for_each_entry(handle_entry, &handle_list, list) {\n\t\t\tadd_inst(handle_entry->name, handle_entry->was_top_instance,\n\t\t\t\t handle_entry->was_top_instance);\n\t\t}\n\t}\n\n\tlist_for_each_entry(inst_entry, &inst_list, list) {\n\t\tlist_for_each_entry(handle_entry, &handle_list, list) {\n\t\t\tif ((inst_entry->was_top_instance &&\n\t\t\t     handle_entry->was_top_instance) ||\n\t\t\t    (!inst_entry->was_top_instance &&\n\t\t\t     !strcmp(handle_entry->name, inst_entry->name))) {\n\t\t\t\tinst_entry->handle = handle_entry;\n\t\t\t\tgoto found;\n\t\t\t}\n\t\t}\n\n\t\twarning(\"Requested instance %s was not found in trace.\", inst_entry->name);\n\t\tbreak;\nfound:\n\t\tcontinue;\n\t}\n}\n\nstatic bool is_top_instance_unique(void)\n{\n\tstruct inst_list *inst_entry;\n\tbool has_top_buffer = false;\n\n\t/* Check there is at most one top buffer. */\n\tlist_for_each_entry(inst_entry, &inst_list, list) {\n\t\tif (inst_entry->is_top_instance) {\n\t\t\tif (has_top_buffer)\n\t\t\t\treturn false;\n\t\t\thas_top_buffer = true;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nenum {\n\tOPT_top = 237,\n};\n\n/*\n * Used to identify the arg. previously parsed.\n * E.g. '-b' can only follow '--top'.\n */\nenum prev_arg_type {\n\tPREV_IS_NONE,\n\tPREV_IS_TOP,\n\tPREV_IS_BUFFER,\n};\n\nvoid trace_split (int argc, char **argv)\n{\n\tstruct tracecmd_input *handle;\n\tunsigned long long start_ns = 0, end_ns = 0;\n\tunsigned long long current;\n\tenum prev_arg_type prev_arg_type;\n\tstruct inst_list *prev_inst = NULL;\n\tint prev_arg_idx;\n\tbool end_reached = false;\n\tdouble start, end;\n\tchar *endptr;\n\tchar *output = NULL;\n\tchar *output_file;\n\tenum split_types split_type = SPLIT_NONE;\n\tenum split_types type = SPLIT_NONE;\n\tint instances;\n\tint count;\n\tint repeat = 0;\n\tint percpu = 0;\n\tint cpu = -1;\n\tint ac;\n\tint c;\n\n\tstatic struct option long_options[] = {\n\t\t{\"top\", optional_argument, NULL, OPT_top},\n\t\t{NULL, 0, NULL, 0},\n\t};\n\tint option_index = 0;\n\n\tprev_arg_type = PREV_IS_NONE;\n\n\tlist_head_init(&handle_list);\n\tlist_head_init(&inst_list);\n\n\tif (strcmp(argv[1], \"split\") != 0)\n\t\tusage(argv);\n\n\twhile ((c = getopt_long(argc - 1, argv + 1, \"+ho:i:s:m:u:e:p:rcC:B:b:t\",\n\t\t\t\tlong_options, &option_index)) >= 0) {\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\ttype++;\n\t\tcase 'e':\n\t\t\ttype++;\n\t\tcase 'u':\n\t\t\ttype++;\n\t\tcase 'm':\n\t\t\ttype++;\n\t\tcase 's':\n\t\t\ttype++;\n\t\t\tif (split_type != SPLIT_NONE)\n\t\t\t\tdie(\"Only one type of split is allowed\");\n\t\t\tcount = atoi(optarg);\n\t\t\tif (count <= 0)\n\t\t\t\tdie(\"Units must be greater than 0\");\n\t\t\tsplit_type = type;\n\n\t\t\t/* Spliting by pages only makes sense per cpu */\n\t\t\tif (type == SPLIT_PAGES)\n\t\t\t\tpercpu = 1;\n\t\t\tbreak;\n\t\tcase 'r':\n\t\t\trepeat = 1;\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\tpercpu = 1;\n\t\t\tbreak;\n\t\tcase 'C':\n\t\t\tcpu = atoi(optarg);\n\t\t\tbreak;\n\t\tcase 'o':\n\t\t\tif (output)\n\t\t\t\tdie(\"only one output file allowed\");\n\t\t\toutput = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\tinput_file = optarg;\n\t\t\tbreak;\n\t\tcase OPT_top:\n\t\t\tprev_arg_type = PREV_IS_TOP;\n\t\t\tprev_arg_idx = optind;\n\t\t\tprev_inst = add_inst(default_top_instance_name, true, true);\n\t\t\tbreak;\n\t\tcase 'b':\n\t\t\t/* 1 as --top takes no argument. */\n\t\t\tif (prev_arg_type != PREV_IS_TOP &&\n\t\t\t    (prev_arg_idx != optind - 1))\n\t\t\t\tusage(argv);\n\t\t\tprev_arg_type = PREV_IS_NONE;\n\n\t\t\tprev_inst->is_top_instance = false;\n\n\t\t\tfree(prev_inst->name);\n\t\t\tprev_inst->name = strdup(optarg);\n\t\t\tif (!prev_inst->name)\n\t\t\t\tdie(\"Failed to duplicate %s\", optarg);\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tprev_arg_type = PREV_IS_BUFFER;\n\t\t\tprev_arg_idx = optind;\n\t\t\tprev_inst = add_inst(optarg, false, false);\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\t/* 2 as -B takes an argument. */\n\t\t\tif (prev_arg_type != PREV_IS_BUFFER &&\n\t\t\t    (prev_arg_idx != optind - 2))\n\t\t\t\tusage(argv);\n\t\t\tprev_arg_type = PREV_IS_NONE;\n\n\t\t\tprev_inst->is_top_instance = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tif (!is_top_instance_unique())\n\t\tdie(\"Can only have one top instance.\");\n\n\tac = (argc - optind);\n\n\tif (ac >= 2) {\n\t\toptind++;\n\t\tstart = strtod(argv[optind], &endptr);\n\t\tif (ac > 3)\n\t\t\tusage(argv);\n\n\t\t/* Make sure a true start value was entered */\n\t\tif (*endptr != 0)\n\t\t\tdie(\"Start value not floating point: %s\", argv[optind]);\n\n\t\tstart_ns = (unsigned long long)(start * 1000000000.0);\n\t\toptind++;\n\t\tif (ac == 3) {\n\t\t\tend = strtod(argv[optind], &endptr);\n\n\t\t\t/* Make sure a true end value was entered */\n\t\t\tif (*endptr != 0)\n\t\t\t\tdie(\"End value not floating point: %s\",\n\t\t\t\t    argv[optind]);\n\n\t\t\tend_ns = (unsigned long long)(end * 1000000000.0);\n\t\t\tif (end_ns < start_ns)\n\t\t\t\tdie(\"Error: end is less than start\");\n\t\t}\n\t}\n\n\tif (!input_file)\n\t\tinput_file = default_input_file;\n\n\thandle = tracecmd_open(input_file, 0);\n\tif (!handle)\n\t\tdie(\"error reading %s\", input_file);\n\n\tif (tracecmd_get_file_state(handle) == TRACECMD_FILE_CPU_LATENCY)\n\t\tdie(\"trace-cmd split does not work with latency traces\\n\");\n\n\tpage_size = tracecmd_page_size(handle);\n\n\tif (!output)\n\t\toutput = strdup(input_file);\n\n\tif (!repeat && strcmp(output, input_file) == 0) {\n\t\toutput = realloc(output, strlen(output) + 3);\n\t\tstrcat(output, \".1\");\n\t}\n\n\toutput_file = malloc(strlen(output) + 50);\n\tif (!output_file)\n\t\tdie(\"Failed to allocate for %s\", output);\n\tc = 1;\n\n\tadd_handle(default_top_instance_name, -1, true);\n\tinstances = tracecmd_buffer_instances(handle);\n\tif (instances) {\n\t\tconst char *name;\n\t\tint i;\n\n\t\tfor (i = 0; i < instances; i++) {\n\t\t\tname = tracecmd_buffer_instance_name(handle, i);\n\t\t\tif (!name)\n\t\t\t\tdie(\"error in reading buffer instance\");\n\t\t\tadd_handle(name, i, false);\n\t\t}\n\t}\n\n\tmap_inst_handle();\n\n\tdo {\n\t\tif (repeat)\n\t\t\tsprintf(output_file, \"%s.%04d\", output, c++);\n\t\telse\n\t\t\tstrcpy(output_file, output);\n\t\t\t\n\t\tcurrent = parse_file(handle, output_file, start_ns, end_ns,\n\t\t\t\t     percpu, cpu, count, type, &end_reached);\n\n\t\tif (!repeat)\n\t\t\tbreak;\n\t\tstart_ns = 0;\n\t} while (!end_reached && (current && (!end_ns || current < end_ns)));\n\n\tfree(output);\n\tfree(output_file);\n\n\ttracecmd_close(handle);\n\tfree_handles(&handle_list);\n\tfree_inst(&inst_list);\n\n\treturn;\n}\n"
  },
  {
    "path": "tracecmd/trace-sqlhist.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n#include <errno.h>\n#include <unistd.h>\n#include <tracefs.h>\n\n#include \"trace-local.h\"\n\nenum action {\n\tACTION_DEFAULT\t= 0,\n\tACTION_SNAPSHOT\t= (1 << 0),\n\tACTION_TRACE\t= (1 << 1),\n\tACTION_SAVE\t= (1 << 2),\n\tACTION_MAX\t= (1 << 3),\n\tACTION_CHANGE\t= (1 << 4),\n};\n\n#define ACTIONS ((ACTION_MAX - 1))\n\nstatic int do_sql(const char *instance_name,\n\t\t  const char *buffer, const char *name, const char *var,\n\t\t  const char *trace_dir, bool execute, int action,\n\t\t  char **save_fields)\n{\n\tstruct tracefs_synth *synth;\n\tstruct tep_handle *tep;\n\tstruct trace_seq seq;\n\tenum tracefs_synth_handler handler;\n\tchar *err = NULL;\n\tint ret;\n\n\tif ((action & ACTIONS) && !var)\n\t\tdie(\"Error: -s, -S and -T not supported without -m or -c\");\n\n\tif (!name)\n\t\tname = \"Anonymous\";\n\n\ttrace_seq_init(&seq);\n\ttep = tracefs_local_events(trace_dir);\n\tif (!tep)\n\t\tdie(\"Could not read %s\", trace_dir ? trace_dir : \"tracefs directory\");\n\n\tsynth = tracefs_sql(tep, name, buffer, &err);\n\tif (!synth)\n\t\tdie(\"Failed creating synthetic event!\\n%s\", err ? err : \"\");\n\n\tif (tracefs_synth_complete(synth)) {\n\t\tif (var) {\n\t\t\tif (action & ACTION_MAX)\n\t\t\t\thandler = TRACEFS_SYNTH_HANDLE_MAX;\n\t\t\telse\n\t\t\t\thandler = TRACEFS_SYNTH_HANDLE_CHANGE;\n\n\t\t\t/* Default to trace if other actions are not set */\n\t\t\tif (!(action & (ACTION_SAVE | ACTION_SNAPSHOT)))\n\t\t\t\taction |= ACTION_TRACE;\n\n\t\t\tif (action & ACTION_SAVE) {\n\t\t\t\tret = tracefs_synth_save(synth, handler, var, save_fields);\n\t\t\t\tif (ret < 0) {\n\t\t\t\t\terr = \"adding save\";\n\t\t\t\t\tgoto failed_action;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (action & ACTION_TRACE) {\n\t\t\t\t/*\n\t\t\t\t * By doing the trace before snapshot, it will be included\n\t\t\t\t * in the snapshot.\n\t\t\t\t */\n\t\t\t\tret = tracefs_synth_trace(synth, handler, var);\n\t\t\t\tif (ret < 0) {\n\t\t\t\t\terr = \"adding trace\";\n\t\t\t\t\tgoto failed_action;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (action & ACTION_SNAPSHOT) {\n\t\t\t\tret = tracefs_synth_snapshot(synth, handler, var);\n\t\t\t\tif (ret < 0) {\n\t\t\t\t\terr = \"adding snapshot\";\n failed_action:\n\t\t\t\t\tperror(err);\n\t\t\t\t\tif (errno == ENODEV)\n\t\t\t\t\t\tfprintf(stderr, \"ERROR: '%s' is not a variable\\n\",\n\t\t\t\t\t\t\tvar);\n\t\t\t\t\texit(-1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttracefs_synth_echo_cmd(&seq, synth);\n\t\tif (execute) {\n\t\t\tret = tracefs_synth_create(synth);\n\t\t\tif (ret < 0)\n\t\t\t\tdie(\"%s\\n\", tracefs_error_last(NULL));\n\t\t}\n\t} else {\n\t\tstruct tracefs_instance *instance = NULL;\n\t\tstruct tracefs_hist *hist;\n\n\t\thist = tracefs_synth_get_start_hist(synth);\n\t\tif (!hist)\n\t\t\tdie(\"get_start_hist\");\n\n\t\tif (instance_name) {\n\t\t\tif (execute)\n\t\t\t\tinstance = tracefs_instance_create(instance_name);\n\t\t\telse\n\t\t\t\tinstance = tracefs_instance_alloc(trace_dir,\n\t\t\t\t\t\t\t\t  instance_name);\n\t\t\tif (!instance)\n\t\t\t\tdie(\"Failed to create instance\");\n\t\t}\n\t\ttracefs_hist_echo_cmd(&seq, instance, hist, 0);\n\t\tif (execute) {\n\t\t\tret = tracefs_hist_start(instance, hist);\n\t\t\tif (ret < 0)\n\t\t\t\tdie(\"%s\\n\", tracefs_error_last(instance));\n\t\t}\n\t}\n\n\ttracefs_synth_free(synth);\n\n\ttrace_seq_do_printf(&seq);\n\ttrace_seq_destroy(&seq);\n\treturn 0;\n}\n\nvoid trace_sqlhist (int argc, char **argv)\n{\n\tchar *trace_dir = NULL;\n\tchar *buffer = NULL;\n\tchar buf[BUFSIZ];\n\tint buffer_size = 0;\n\tconst char *file = NULL;\n\tconst char *instance = NULL;\n\tbool execute = false;\n\tchar **save_fields = NULL;\n\tconst char *name = NULL;\n\tconst char *var = NULL;\n\tchar **save_argv;\n\tint action = 0;\n\tchar *tok;\n\tFILE *fp;\n\tsize_t r;\n\tint c;\n\tint i;\n\n\t/* Remove 'trace-cmd' */\n\tsave_argv = argv;\n\targc -= 1;\n\targv += 1;\n\n\tif (argc < 2)\n\t\tusage(save_argv);\n\n\tfor (;;) {\n\t\tc = getopt(argc, argv, \"ht:f:en:m:c:sS:TB:\");\n\t\tif (c == -1)\n\t\t\tbreak;\n\n\t\tswitch(c) {\n\t\tcase 'h':\n\t\t\tusage(save_argv);\n\t\tcase 't':\n\t\t\ttrace_dir = optarg;\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tfile = optarg;\n\t\t\tbreak;\n\t\tcase 'e':\n\t\t\texecute = true;\n\t\t\tbreak;\n\t\tcase 'm':\n\t\t\taction |= ACTION_MAX;\n\t\t\tvar = optarg;\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\taction |= ACTION_CHANGE;\n\t\t\tvar = optarg;\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\taction |= ACTION_SNAPSHOT;\n\t\t\tbreak;\n\t\tcase 'S':\n\t\t\taction |= ACTION_SAVE;\n\t\t\ttok = strtok(optarg, \",\");\n\t\t\twhile (tok) {\n\t\t\t\tsave_fields = tracefs_list_add(save_fields, tok);\n\t\t\t\ttok = strtok(NULL, \",\");\n\t\t\t}\n\t\t\tif (!save_fields) {\n\t\t\t\tperror(optarg);\n\t\t\t\texit(-1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'T':\n\t\t\taction |= ACTION_TRACE | ACTION_SNAPSHOT;\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tinstance = optarg;\n\t\t\tbreak;\n\t\tcase 'n':\n\t\t\tname = optarg;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif ((action & (ACTION_MAX|ACTION_CHANGE)) == (ACTION_MAX|ACTION_CHANGE)) {\n\t\tfprintf(stderr, \"Can not use both -m and -c together\\n\");\n\t\texit(-1);\n\t}\n\tif (file) {\n\t\tif (!strcmp(file, \"-\"))\n\t\t\tfp = stdin;\n\t\telse\n\t\t\tfp = fopen(file, \"r\");\n\t\tif (!fp) {\n\t\t\tperror(file);\n\t\t\texit(-1);\n\t\t}\n\t\twhile ((r = fread(buf, 1, BUFSIZ, fp)) > 0) {\n\t\t\tbuffer = realloc(buffer, buffer_size + r + 1);\n\t\t\tstrncpy(buffer + buffer_size, buf, r);\n\t\t\tbuffer_size += r;\n\t\t}\n\t\tfclose(fp);\n\t\tif (buffer_size)\n\t\t\tbuffer[buffer_size] = '\\0';\n\t} else if (argc == optind) {\n\t\tusage(save_argv);\n\t} else {\n\t\tfor (i = optind; i < argc; i++) {\n\t\t\tr = strlen(argv[i]);\n\t\t\tbuffer = realloc(buffer, buffer_size + r + 2);\n\t\t\tif (i != optind)\n\t\t\t\tbuffer[buffer_size++] = ' ';\n\t\t\tstrcpy(buffer + buffer_size, argv[i]);\n\t\t\tbuffer_size += r;\n\t\t}\n\t}\n\n\tdo_sql(instance, buffer, name, var, trace_dir, execute, action, save_fields);\n\tfree(buffer);\n}\n\n"
  },
  {
    "path": "tracecmd/trace-stack.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n#include <getopt.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <errno.h>\n\n#include \"tracefs.h\"\n#include \"trace-local.h\"\n\n#define PROC_FILE \"/proc/sys/kernel/stack_tracer_enabled\"\n\nenum stack_type {\n\tSTACK_START,\n\tSTACK_STOP,\n\tSTACK_RESET,\n\tSTACK_REPORT\n};\n\nstatic void test_available(void)\n{\n\tstruct stat buf;\n\tint fd;\n\n\tfd = stat(PROC_FILE, &buf);\n\tif (fd < 0)\n\t\tdie(\"stack tracer not configured on running kernel\");\n}\n\n/* NOTE: this implementation only accepts new_status in the range [0..9]. */\nstatic void change_stack_tracer_status(unsigned new_status)\n{\n\tchar buf[1];\n\tint status;\n\tint ret;\n\tint fd;\n\tint n;\n\n\tif (new_status > 9) {\n\t\twarning(\"invalid status %d\\n\", new_status);\n\t\treturn;\n\t}\n\n\tret = tracecmd_stack_tracer_status(&status);\n\tif (ret < 0)\n\t\tdie(\"error reading %s\", PROC_FILE);\n\n\tif (ret > 0 && status == new_status)\n\t\treturn; /* nothing to do */\n\n\tfd = open(PROC_FILE, O_WRONLY);\n\tif (fd < 0)\n\t\tdie(\"writing %s\", PROC_FILE);\n\n\tbuf[0] = new_status + '0';\n\n\tn = write(fd, buf, 1);\n\tif (n < 0)\n\t\tdie(\"writing into %s\", PROC_FILE);\n\tclose(fd);\n}\n\nstatic void start_trace(void)\n{\n\tchange_stack_tracer_status(1);\n}\n\nstatic void stop_trace(void)\n{\n\tchange_stack_tracer_status(0);\n}\n\nstatic void reset_trace(void)\n{\n\tchar *path;\n\tchar buf[1];\n\tint fd;\n\tint n;\n\n\tpath = tracefs_get_tracing_file(\"stack_max_size\");\n\tfd = open(path, O_WRONLY);\n\tif (fd < 0)\n\t\tdie(\"writing %s\", path);\n\n\tbuf[0] = '0';\n\tn = write(fd, buf, 1);\n\tif (n < 0)\n\t\tdie(\"writing into %s\", path);\n\ttracefs_put_tracing_file(path);\n\tclose(fd);\n}\n\nstatic void read_trace(void)\n{\n\tchar *buf = NULL;\n\tint status;\n\tchar *path;\n\tFILE *fp;\n\tsize_t n;\n\tint r;\n\n\tif (tracecmd_stack_tracer_status(&status) <= 0)\n\t\tdie(\"Invalid stack tracer state\");\n\n\tif (status > 0)\n\t\tprintf(\"(stack tracer running)\\n\");\n\telse\n\t\tprintf(\"(stack tracer not running)\\n\");\n\n\tpath = tracefs_get_tracing_file(\"stack_trace\");\n\tfp = fopen(path, \"r\");\n\tif (!fp)\n\t\tdie(\"reading to '%s'\", path);\n\ttracefs_put_tracing_file(path);\n\n\twhile ((r = getline(&buf, &n, fp)) >= 0) {\n\t\t/*\n\t\t * Skip any line that starts with a '#'.\n\t\t * Those talk about how to enable stack tracing\n\t\t * within the debugfs system. We don't care about that.\n\t\t */\n\t\tif (buf[0] != '#')\n\t\t\tprintf(\"%s\", buf);\n\n\t\tfree(buf);\n\t\tbuf = NULL;\n\t}\n\n\tfclose(fp);\n}\n\nenum {\n\tOPT_verbose\t= 252,\n\tOPT_reset\t= 253,\n\tOPT_stop\t= 254,\n\tOPT_start\t= 255,\n};\n\nvoid trace_stack (int argc, char **argv)\n{\n\tenum stack_type trace_type = STACK_REPORT;\n\tint c;\n\n\tif (argc < 2)\n\t\tusage(argv);\n\n\tif (strcmp(argv[1], \"stack\") != 0)\n\t\tusage(argv);\n\n\tfor (;;) {\n\t\tint option_index = 0;\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"start\", no_argument, NULL, OPT_start},\n\t\t\t{\"stop\", no_argument, NULL, OPT_stop},\n\t\t\t{\"reset\", no_argument, NULL, OPT_reset},\n\t\t\t{\"help\", no_argument, NULL, '?'},\n\t\t\t{\"verbose\", optional_argument, NULL, OPT_verbose},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long (argc-1, argv+1, \"+h?\",\n\t\t\tlong_options, &option_index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase OPT_start:\n\t\t\ttrace_type = STACK_START;\n\t\t\tbreak;\n\t\tcase OPT_stop:\n\t\t\ttrace_type = STACK_STOP;\n\t\t\tbreak;\n\t\tcase OPT_reset:\n\t\t\ttrace_type = STACK_RESET;\n\t\t\tbreak;\n\t\tcase OPT_verbose:\n\t\t\tif (trace_set_verbose(optarg) < 0)\n\t\t\t\tdie(\"invalid verbose level %s\", optarg);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\ttest_available();\n\n\tswitch (trace_type) {\n\tcase STACK_START:\n\t\tstart_trace();\n\t\tbreak;\n\tcase STACK_STOP:\n\t\tstop_trace();\n\t\tbreak;\n\tcase STACK_RESET:\n\t\treset_trace();\n\t\tbreak;\n\tdefault:\n\t\tread_trace();\n\t\tbreak;\n\t}\n\n\treturn;\n}\n"
  },
  {
    "path": "tracecmd/trace-stat.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <getopt.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <ctype.h>\n\n#include \"tracefs.h\"\n#include \"trace-local.h\"\n\n#ifndef BUFSIZ\n#define BUFSIZ 1024\n#endif\n\nstatic inline int is_top_instance(struct buffer_instance *instance)\n{\n\treturn instance == &top_instance;\n}\n\nchar *strstrip(char *str)\n{\n\tchar *s;\n\n\tif (!str)\n\t\treturn NULL;\n\n\ts = str + strlen(str) - 1;\n\twhile (s >= str && isspace(*s))\n\t\ts--;\n\ts++;\n\t*s = '\\0';\n\n\tfor (s = str; *s && isspace(*s); s++)\n\t\t;\n\n\treturn s;\n}\n\n/* FIXME: append_file() is duplicated and could be consolidated */\nchar *append_file(const char *dir, const char *name)\n{\n\tchar *file;\n\tint ret;\n\n\tret = asprintf(&file, \"%s/%s\", dir, name);\n\tif (ret < 0)\n\t\tdie(\"Failed to allocate %s/%s\", dir, name);\n\n\treturn file;\n}\n\nstatic char *get_fd_content(int fd, const char *file)\n{\n\tsize_t total = 0;\n\tsize_t alloc;\n\tchar *str = NULL;\n\tint ret;\n\n\tfor (;;) {\n\t\talloc = ((total + BUFSIZ) / BUFSIZ) * BUFSIZ;\n\t\tstr = realloc(str, alloc + 1);\n\t\tif (!str)\n\t\t\tdie(\"malloc\");\n\t\tret = read(fd, str + total, alloc - total);\n\t\tif (ret < 0)\n\t\t\tdie(\"reading %s\\n\", file);\n\t\ttotal += ret;\n\t\tif (!ret)\n\t\t\tbreak;\n\t}\n\tstr[total] = 0;\n\n\treturn str;\n}\n\nchar *get_file_content(const char *file)\n{\n\tchar *str;\n\tint fd;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd < 0)\n\t\treturn NULL;\n\n\tstr = get_fd_content(fd, file);\n\tclose(fd);\n\n\treturn str;\n}\n\nstatic void report_file(struct buffer_instance *instance,\n\t\t\tchar *name, char *def_value, char *description)\n{\n\tchar *str;\n\tchar *cont;\n\n\tif (!tracefs_file_exists(instance->tracefs, name))\n\t\treturn;\n\tstr = tracefs_instance_file_read(instance->tracefs, name, NULL);\n\tif (!str)\n\t\treturn;\n\tcont = strstrip(str);\n\tif (cont[0] && strcmp(cont, def_value) != 0)\n\t\tprintf(\"\\n%s%s\\n\", description, cont);\n\n\tfree(str);\n}\n\nstatic void report_instances(void)\n{\n\tchar **list;\n\tint i;\n\n\tlist = tracefs_instances(NULL);\n\n\tif (!list || !list[0])\n\t\tgoto out;\n\n\tprintf(\"\\nInstances:\\n\");\n\n\tfor (i = 0; list[i]; i++)\n\t\tprintf(\" %s\\n\", list[i]);\n\n out:\n\ttracefs_list_free(list);\n}\n\nstruct event_iter *trace_event_iter_alloc(const char *path)\n{\n\tstruct event_iter *iter;\n\n\titer = malloc(sizeof(*iter));\n\tif (!iter)\n\t\tdie(\"Failed to allocate event_iter for path %s\", path);\n\tmemset(iter, 0, sizeof(*iter));\n\n\titer->system_dir = opendir(path);\n\tif (!iter->system_dir)\n\t\tdie(\"opendir\");\n\n\treturn iter;\n}\n\nenum event_iter_type\ntrace_event_iter_next(struct event_iter *iter, const char *path, const char *system)\n{\n\tstruct dirent *dent;\n\n\tif (system && !iter->event_dir) {\n\t\tchar *event;\n\t\tstruct stat st;\n\n\t\tevent = append_file(path, system);\n\n\t\tstat(event, &st);\n\t\tif (!S_ISDIR(st.st_mode)) {\n\t\t\tfree(event);\n\t\t\tgoto do_system;\n\t\t}\n\n\t\titer->event_dir = opendir(event);\n\t\tif (!iter->event_dir)\n\t\t\tdie(\"opendir %s\", event);\n\t\tfree(event);\n\t}\n\n\tif (iter->event_dir) {\n\t\twhile ((dent = readdir(iter->event_dir))) {\n\t\t\tconst char *name = dent->d_name;\n\n\t\t\tif (strcmp(name, \".\") == 0 ||\n\t\t\t    strcmp(name, \"..\") == 0)\n\t\t\t\tcontinue;\n\n\t\t\titer->event_dent = dent;\n\t\t\treturn EVENT_ITER_EVENT;\n\t\t}\n\t\tclosedir(iter->event_dir);\n\t\titer->event_dir = NULL;\n\t}\n\n do_system:\n\twhile ((dent = readdir(iter->system_dir))) {\n\t\tconst char *name = dent->d_name;\n\n\t\tif (strcmp(name, \".\") == 0 ||\n\t\t    strcmp(name, \"..\") == 0)\n\t\t\tcontinue;\n\n\t\titer->system_dent = dent;\n\n\t\treturn EVENT_ITER_SYSTEM;\n\t}\n\n\treturn EVENT_ITER_NONE;\n}\n\nvoid trace_event_iter_free(struct event_iter *iter)\n{\n\tif (!iter)\n\t\treturn;\n\n\tif (iter->event_dir)\n\t\tclosedir(iter->event_dir);\n\n\tclosedir(iter->system_dir);\n\tfree(iter);\n}\n\nstatic void reset_event_iter(struct event_iter *iter)\n{\n\tif (iter->event_dir) {\n\t\tclosedir(iter->event_dir);\n\t\titer->event_dir = NULL;\n\t}\n\n\trewinddir(iter->system_dir);\n}\n\nstatic int process_individual_events(const char *path, struct event_iter *iter)\n{\n\tstruct stat st;\n\tconst char *system = iter->system_dent->d_name;\n\tchar *file;\n\tchar *enable = NULL;\n\tchar *str;\n\tint ret = 0;\n\n\tfile = append_file(path, system);\n\n\tstat(file, &st);\n\tif (!S_ISDIR(st.st_mode))\n\t\tgoto out;\n\n\tenable = append_file(file, \"enable\");\n\tstr = get_file_content(enable);\n\tif (!str)\n\t\tgoto out;\n\n\tif (*str != '1' && *str != '0')\n\t\tret = 1;\n\tfree(str);\n\n out:\n\tfree(enable);\n\tfree(file);\n\n\treturn ret;\n}\n\nstatic void\nprocess_event_enable(char *path, const char *system, const char *name,\n\t\t     enum event_process *processed)\n{\n\tstruct stat st;\n\tchar *enable = NULL;\n\tchar *file;\n\tchar *str;\n\n\tif (system)\n\t\tpath = append_file(path, system);\n\n\tfile = append_file(path, name);\n\n\tif (system)\n\t\tfree(path);\n\n\tstat(file, &st);\n\tif (!S_ISDIR(st.st_mode))\n\t\tgoto out;\n\n\tenable = append_file(file, \"enable\");\n\tstr = get_file_content(enable);\n\tif (!str)\n\t\tgoto out;\n\n\tif (*str == '1') {\n\t\tif (!system) {\n\t\t\tif (!*processed)\n\t\t\t\tprintf(\" Individual systems:\\n\");\n\t\t\tprintf( \"   %s\\n\", name);\n\t\t\t*processed = PROCESSED_SYSTEM;\n\t\t} else {\n\t\t\tif (!*processed) {\n\t\t\t\tprintf(\" Individual events:\\n\");\n\t\t\t\t*processed = PROCESSED_SYSTEM;\n\t\t\t}\n\t\t\tif (*processed == PROCESSED_SYSTEM) {\n\t\t\t\tprintf(\"    %s\\n\", system);\n\t\t\t\t*processed = PROCESSED_EVENT;\n\t\t\t}\n\t\t\tprintf( \"        %s\\n\", name);\n\t\t}\n\t}\n\tfree(str);\n\n out:\n\tfree(enable);\n\tfree(file);\n}\n\nstatic void report_events(struct buffer_instance *instance)\n{\n\tstruct event_iter *iter;\n\tchar *str;\n\tchar *cont;\n\tchar *path;\n\tchar *system;\n\tenum event_iter_type type;\n\tenum event_process processed = PROCESSED_NONE;\n\tenum event_process processed_part = PROCESSED_NONE;\n\n\tstr = tracefs_instance_file_read(instance->tracefs, \"events/enable\", NULL);\n\tif (!str)\n\t\treturn;\n\n\tcont = strstrip(str);\n\n\tprintf(\"\\nEvents:\\n\");\n\n\tswitch(*cont) {\n\tcase '1':\n\t\tprintf(\" All enabled\\n\");\n\t\tfree(str);\n\t\treturn;\n\tcase '0':\n\t\tprintf(\" All disabled\\n\");\n\t\tfree(str);\n\t\treturn;\n\t}\n\n\tfree(str);\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"events\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\titer = trace_event_iter_alloc(path);\n\n\twhile (trace_event_iter_next(iter, path, NULL)) {\n\t\tprocess_event_enable(path, NULL, iter->system_dent->d_name, &processed);\n\t}\n\n\treset_event_iter(iter);\n\n\tsystem = NULL;\n\twhile ((type = trace_event_iter_next(iter, path, system))) {\n\n\t\tif (type == EVENT_ITER_SYSTEM) {\n\n\t\t\t/* Only process systems that are not fully enabled */\n\t\t\tif (!process_individual_events(path, iter))\n\t\t\t\tcontinue;\n\n\t\t\tsystem = iter->system_dent->d_name;\n\t\t\tif (processed_part)\n\t\t\t\tprocessed_part = PROCESSED_SYSTEM;\n\t\t\tcontinue;\n\t\t}\n\n\t\tprocess_event_enable(path, iter->system_dent->d_name,\n\t\t\t\t     iter->event_dent->d_name, &processed_part);\n\t}\n\n\ttrace_event_iter_free(iter);\n\n\tif (!processed && !processed_part)\n\t\tprintf(\"  (none enabled)\\n\");\n\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void\nprocess_event_filter(char *path, struct event_iter *iter, enum event_process *processed)\n{\n\tconst char *system = iter->system_dent->d_name;\n\tconst char *event = iter->event_dent->d_name;\n\tstruct stat st;\n\tchar *filter = NULL;\n\tchar *file;\n\tchar *str;\n\tchar *cont;\n\n\tpath = append_file(path, system);\n\tfile = append_file(path, event);\n\tfree(path);\n\n\tstat(file, &st);\n\tif (!S_ISDIR(st.st_mode))\n\t\tgoto out;\n\n\tfilter = append_file(file, \"filter\");\n\tstr = get_file_content(filter);\n\tif (!str)\n\t\tgoto out;\n\n\tcont = strstrip(str);\n\n\tif (strcmp(cont, \"none\") == 0) {\n\t\tfree(str);\n\t\tgoto out;\n\t}\n\n\tif (!*processed)\n\t\tprintf(\"\\nFilters:\\n\");\n\tprintf( \"  %s:%s \\\"%s\\\"\\n\", system, event, cont);\n\t*processed = PROCESSED_SYSTEM;\n\tfree(str);\n\n out:\n\tfree(filter);\n\tfree(file);\n}\n\nstatic void report_event_filters(struct buffer_instance *instance)\n{\n\tstruct event_iter *iter;\n\tchar *path;\n\tchar *system;\n\tenum event_iter_type type;\n\tenum event_process processed = PROCESSED_NONE;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"events\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\titer = trace_event_iter_alloc(path);\n\n\tprocessed = PROCESSED_NONE;\n\tsystem = NULL;\n\twhile ((type = trace_event_iter_next(iter, path, system))) {\n\n\t\tif (type == EVENT_ITER_SYSTEM) {\n\t\t\tsystem = iter->system_dent->d_name;\n\t\t\tcontinue;\n\t\t}\n\n\t\tprocess_event_filter(path, iter, &processed);\n\t}\n\n\ttrace_event_iter_free(iter);\n\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void\nprocess_event_trigger(char *path, struct event_iter *iter, enum event_process *processed)\n{\n\tconst char *system = iter->system_dent->d_name;\n\tconst char *event = iter->event_dent->d_name;\n\tstruct stat st;\n\tchar *trigger = NULL;\n\tchar *file;\n\tchar *str;\n\tchar *cont;\n\n\tpath = append_file(path, system);\n\tfile = append_file(path, event);\n\tfree(path);\n\n\tstat(file, &st);\n\tif (!S_ISDIR(st.st_mode))\n\t\tgoto out;\n\n\ttrigger = append_file(file, \"trigger\");\n\tstr = get_file_content(trigger);\n\tif (!str)\n\t\tgoto out;\n\n\tcont = strstrip(str);\n\n\tif (cont[0] == '#') {\n\t\tfree(str);\n\t\tgoto out;\n\t}\n\n\tif (!*processed)\n\t\tprintf(\"\\nTriggers:\\n\");\n\tprintf( \"  %s:%s \\\"%s\\\"\\n\", system, event, cont);\n\t*processed = PROCESSED_SYSTEM;\n\tfree(str);\n\n out:\n\tfree(trigger);\n\tfree(file);\n}\n\nstatic void report_event_triggers(struct buffer_instance *instance)\n{\n\tstruct event_iter *iter;\n\tchar *path;\n\tchar *system;\n\tenum event_iter_type type;\n\tenum event_process processed = PROCESSED_NONE;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"events\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\titer = trace_event_iter_alloc(path);\n\n\tprocessed = PROCESSED_NONE;\n\tsystem = NULL;\n\twhile ((type = trace_event_iter_next(iter, path, system))) {\n\n\t\tif (type == EVENT_ITER_SYSTEM) {\n\t\t\tsystem = iter->system_dent->d_name;\n\t\t\tcontinue;\n\t\t}\n\n\t\tprocess_event_trigger(path, iter, &processed);\n\t}\n\n\ttrace_event_iter_free(iter);\n\n\ttracefs_put_tracing_file(path);\n}\n\nenum func_states {\n\tFUNC_STATE_START,\n\tFUNC_STATE_SKIP,\n\tFUNC_STATE_PRINT,\n};\n\nstatic void list_functions(const char *path, char *string)\n{\n\tenum func_states state;\n\tstruct stat st;\n\tchar *str;\n\tint ret = 0;\n\tint len;\n\tint i;\n\tint first = 0;\n\n\t/* Ignore if it does not exist. */\n\tret = stat(path, &st);\n\tif (ret < 0)\n\t\treturn;\n\n\tstr = get_file_content(path);\n\tif (!str)\n\t\treturn;\n\n\tlen = strlen(str);\n\n\tstate = FUNC_STATE_START;\n\n\t/* Skip all lines that start with '#' */\n\tfor (i = 0; i < len; i++) {\n\n\t\tif (state == FUNC_STATE_PRINT)\n\t\t\tputchar(str[i]);\n\n\t\tif (str[i] == '\\n') {\n\t\t\tstate = FUNC_STATE_START;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (state == FUNC_STATE_SKIP)\n\t\t\tcontinue;\n\n\t\tif (state == FUNC_STATE_START && str[i] == '#') {\n\t\t\tstate = FUNC_STATE_SKIP;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!first) {\n\t\t\tprintf(\"\\n%s:\\n\", string);\n\t\t\tfirst = 1;\n\t\t}\n\n\t\tif (state != FUNC_STATE_PRINT) {\n\t\t\tstate = FUNC_STATE_PRINT;\n\t\t\tprintf(\"   \");\n\t\t\tputchar(str[i]);\n\t\t}\n\t}\n\tfree(str);\n}\n\nstatic void report_graph_funcs(struct buffer_instance *instance)\n{\n\tchar *path;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"set_graph_function\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\tlist_functions(path, \"Function Graph Filter\");\n\t\n\ttracefs_put_tracing_file(path);\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"set_graph_notrace\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\tlist_functions(path, \"Function Graph No Trace\");\n\t\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void report_ftrace_filters(struct buffer_instance *instance)\n{\n\tchar *path;\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"set_ftrace_filter\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\tlist_functions(path, \"Function Filter\");\n\t\n\ttracefs_put_tracing_file(path);\n\n\tpath = tracefs_instance_get_file(instance->tracefs, \"set_ftrace_notrace\");\n\tif (!path)\n\t\tdie(\"malloc\");\n\n\tlist_functions(path, \"Function No Trace\");\n\t\n\ttracefs_put_tracing_file(path);\n}\n\nstatic void report_buffers(struct buffer_instance *instance)\n{\n#define FILE_SIZE 100\n\tchar *str;\n\tchar *cont;\n\tchar file[FILE_SIZE];\n\tint pagesize;\n\tint bufsize;\n\tint cpu;\n\n\tstr = tracefs_instance_file_read(instance->tracefs, \"buffer_size_kb\", NULL);\n\tif (!str)\n\t\treturn;\n\n\tcont = strstrip(str);\n\n\t/* If it's not expanded yet, just skip */\n\tif (strstr(cont, \"expanded\") != NULL)\n\t\tgoto out;\n\n\tif (strcmp(cont, \"X\") != 0) {\n\t\tprintf(\"\\nBuffer size in kilobytes (per cpu):\\n\");\n\t\tprintf(\"   %s\\n\", str);\n\t\tgoto total;\n\t}\n\n\t/* Read the sizes of each CPU buffer */\n\tfor (cpu = 0; ; cpu++) {\n\n\t\tsnprintf(file, FILE_SIZE, \"per_cpu/cpu%d/buffer_size_kb\", cpu);\n\t\tstr = tracefs_instance_file_read(instance->tracefs, file, NULL);\n\t\tif (!str)\n\t\t\tbreak;\n\n\t\tcont = strstrip(str);\n\t\tif (!cpu)\n\t\t\tputchar('\\n');\n\n\t\tprintf(\"CPU %d buffer size (kb): %s\\n\", cpu, cont);\n\t\tfree(str);\n\t}\n\n total:\n\tfree(str);\n\n\tstr = tracefs_instance_file_read(instance->tracefs, \"buffer_total_size_kb\", NULL);\n\tif (!str)\n\t\treturn;\n\n\tcont = strstrip(str);\n\tprintf(\"\\nBuffer total size in kilobytes:\\n\");\n\tprintf(\"   %s\\n\", str);\n\n\tpagesize = getpagesize();\n\tbufsize = tracefs_instance_get_subbuf_size(instance->tracefs);\n\tif (bufsize > 0 && bufsize * 1024 != pagesize)\n\t\tprintf(\"\\nSub-buffer size in kilobytes:\\n   %d\\n\", bufsize);\n\n out:\n\tfree(str);\n}\n\nstatic void report_clock(struct buffer_instance *instance)\n{\n\tstruct tracefs_instance *tracefs = instance ? instance->tracefs : NULL;\n\tchar *clock;\n\n\tclock = tracefs_get_clock(tracefs);\n\n\t/* Default clock is \"local\", only show others */\n\tif (clock && strcmp(clock, \"local\") != 0)\n\t\tprintf(\"\\nClock: %s\\n\", clock);\n\n\tfree(clock);\n}\n\nstatic void report_cpumask(struct buffer_instance *instance)\n{\n\tchar *str;\n\tchar *cont;\n\tint cpus;\n\tint n;\n\tint i;\n\n\tstr = tracefs_instance_file_read(instance->tracefs, \"tracing_cpumask\", NULL);\n\tif (!str)\n\t\treturn;\n\n\tcont = strstrip(str);\n\n\t/* check to make sure all CPUs on this machine are set */\n\tcpus = tracecmd_count_cpus();\n\n\tfor (i = strlen(cont) - 1; i >= 0 && cpus > 0; i--) {\n\t\tif (cont[i] == ',')\n\t\t\tcontinue;\n\n\t\tif (cont[i] == 'f') {\n\t\t\tcpus -= 4;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (cpus >= 4)\n\t\t\tbreak;\n\n\t\tif (cont[i] >= '0' && cont[i] <= '9')\n\t\t\tn = cont[i] - '0';\n\t\telse\n\t\t\tn = 10 + (cont[i] - 'a');\n\n\t\twhile (cpus > 0) {\n\t\t\tif (!(n & 1))\n\t\t\t\tbreak;\n\t\t\tn >>= 1;\n\t\t\tcpus--;\n\t\t}\n\t\tbreak;\n\t}\n\n\t/* If cpus is greater than zero, one isn't set */\n\tif (cpus > 0)\n\t\tprintf(\"\\nCPU mask: %s\\n\", cont);\n\n\tfree(str);\n}\n\nstatic void report_probes(struct buffer_instance *instance,\n\t\t\t  const char *file, const char *string)\n{\n\tchar *str;\n\tchar *cont;\n\tint newline;\n\tint i;\n\n\tstr = tracefs_instance_file_read(instance->tracefs, file, NULL);\n\tif (!str)\n\t\treturn;\n\n\tcont = strstrip(str);\n\tif (strlen(cont) == 0)\n\t\tgoto out;\n\n\tprintf(\"\\n%s:\\n\", string);\n\n\tnewline = 1;\n\tfor (i = 0; cont[i]; i++) {\n\t\tif (newline)\n\t\t\tprintf(\"   \");\n\t\tputchar(cont[i]);\n\t\tif (cont[i] == '\\n')\n\t\t\tnewline = 1;\n\t\telse\n\t\t\tnewline = 0;\n\t}\n\tputchar('\\n');\n out:\n\tfree(str);\n}\n\nstatic void report_kprobes(struct buffer_instance *instance)\n{\n\treport_probes(instance, \"kprobe_events\", \"Kprobe events\");\n}\n\nstatic void report_uprobes(struct buffer_instance *instance)\n{\n\treport_probes(instance, \"uprobe_events\", \"Uprobe events\");\n}\n\nstatic void report_synthetic(struct buffer_instance *instance)\n{\n\treport_probes(instance, \"synthetic_events\", \"Synthetic events\");\n}\n\nstatic void report_traceon(struct buffer_instance *instance)\n{\n\tchar *str;\n\tchar *cont;\n\n\tstr = tracefs_instance_file_read(instance->tracefs, \"tracing_on\", NULL);\n\tif (!str)\n\t\treturn;\n\n\tcont = strstrip(str);\n\n\t/* double newline as this is the last thing printed */\n\tif (strcmp(cont, \"0\") == 0)\n\t\tprintf(\"\\nTracing is disabled\\n\\n\");\n\telse\n\t\tprintf(\"\\nTracing is enabled\\n\\n\");\n\n\tfree(str);\n}\n\nstatic void stat_instance(struct buffer_instance *instance, bool opt)\n{\n\tif (instance != &top_instance) {\n\t\tif (instance != first_instance)\n\t\t\tprintf(\"---------------\\n\");\n\t\tprintf(\"Instance: %s\\n\",\n\t\t\ttracefs_instance_get_name(instance->tracefs));\n\t}\n\n\treport_file(instance, \"current_tracer\", \"nop\", \"Tracer: \");\n\treport_events(instance);\n\treport_event_filters(instance);\n\treport_event_triggers(instance);\n\treport_ftrace_filters(instance);\n\treport_graph_funcs(instance);\n\treport_buffers(instance);\n\treport_clock(instance);\n\treport_cpumask(instance);\n\treport_file(instance, \"tracing_max_latency\", \"0\", \"Max Latency: \");\n\treport_kprobes(instance);\n\treport_uprobes(instance);\n\treport_synthetic(instance);\n\treport_file(instance, \"set_event_pid\", \"\", \"Filtered event PIDs:\\n\");\n\treport_file(instance, \"set_event_notrace_pid\", \"\", \"Filtered notrace event PIDs:\\n\");\n\treport_file(instance, \"set_ftrace_pid\", \"no pid\",\n\t\t    \"Filtered function tracer PIDs:\\n\");\n\treport_file(instance, \"set_ftrace_notrace_pid\", \"no pid\",\n\t\t    \"Filtered function tracer notrace PIDs:\\n\");\n\tif (opt) {\n\t\tprintf(\"\\nOptions:\\n\");\n\t\tshow_options(\"   \", instance, NULL);\n\t}\n\treport_traceon(instance);\n\treport_file(instance, \"error_log\", \"\", \"Error log:\\n\");\n\tif (instance == &top_instance)\n\t\treport_instances();\n}\n\nvoid trace_stat (int argc, char **argv)\n{\n\tstruct buffer_instance *instance = &top_instance;\n\tbool opt = false;\n\tint topt = 0;\n\tint status;\n\tint c;\n\n\tinit_top_instance();\n\n\tfor (;;) {\n\t\tc = getopt(argc-1, argv+1, \"htoB:\");\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'h':\n\t\t\tusage(argv);\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tinstance = allocate_instance(optarg);\n\t\t\tif (!instance)\n\t\t\t\tdie(\"Failed to create instance\");\n\t\t\tadd_instance(instance, tracecmd_count_cpus());\n\t\t\t/* top instance requires direct access */\n\t\t\tif (!topt && is_top_instance(first_instance))\n\t\t\t\tfirst_instance = instance;\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\t/* Force to use top instance */\n\t\t\ttopt = 1;\n\t\t\tinstance = &top_instance;\n\t\t\tbreak;\n\t\tcase 'o':\n\t\t\topt = 1;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tusage(argv);\n\t\t}\n\t}\n\n\tupdate_first_instance(instance, topt);\n\n\tfor_all_instances(instance) {\n\t\tstat_instance(instance, opt);\n\t}\n\n\tif (tracecmd_stack_tracer_status(&status) >= 0) {\n\t\tif (status > 0)\n\t\t\tprintf(\"Stack tracing is enabled\\n\\n\");\n\t} else {\n\t\tprintf(\"Error reading stack tracer status\\n\\n\");\n\t}\n\n\texit(0);\n}\n"
  },
  {
    "path": "tracecmd/trace-stream.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n *\n */\n#include <stdio.h>\n#include <poll.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <errno.h>\n\n#include <sys/time.h>\n#include <sys/types.h>\n\n#include \"trace-local.h\"\n\n/*\n * Stream runs for a single machine. We are going to cheat\n * and use the trace-output and trace-input code to create\n * our pevent. First just create a trace.dat file and then read\n * it to create the pevent and handle.\n */\nstruct tracecmd_input *\ntrace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus,\n\t\t  struct hook_list *hooks,\n\t\t  tracecmd_handle_init_func handle_init, int global)\n{\n\tstruct tracecmd_output *trace_output;\n\tstruct tracecmd_input *trace_input;\n\tstatic FILE *fp = NULL;\n\tstatic int tfd;\n\tlong flags;\n\n\tif (instance->handle) {\n\t\ttrace_input = instance->handle;\n\t\tgoto make_pipe;\n\t}\n\n\tif (!fp) {\n\t\tfp = tmpfile();\n\t\tif (!fp)\n\t\t\treturn NULL;\n\t\ttfd = fileno(fp);\n\n\t\ttrace_output = tracecmd_output_create_fd(tfd);\n\t\tif (!trace_output)\n\t\t\tgoto fail;\n\n\t\ttracecmd_output_write_headers(trace_output, NULL);\n\t\ttracecmd_output_flush(trace_output);\n\t\t/* Don't close the descriptor, use it for reading */\n\t\ttracecmd_output_free(trace_output);\n\t}\n\n\tlseek(tfd, 0, SEEK_SET);\n\n\ttrace_input = tracecmd_alloc_fd(tfd, 0);\n\tif (!trace_input)\n\t\tgoto fail;\n\n\tif (tracecmd_read_headers(trace_input, TRACECMD_FILE_PRINTK) < 0)\n\t\tgoto fail_free_input;\n\n\tif (handle_init)\n\t\thandle_init(trace_input, hooks, global);\n\n make_pipe:\n\t/* Do not block on this pipe */\n\tflags = fcntl(fd, F_GETFL);\n\tfcntl(fd, F_SETFL, flags | O_NONBLOCK);\n\n\tif (tracecmd_make_pipe(trace_input, cpu, fd, cpus) < 0)\n\t\tgoto fail_free_input;\n\n\tinstance->handle = trace_input;\n\n\treturn trace_input;\n\n fail_free_input:\n\ttracecmd_close(trace_input);\n fail:\n\tfclose(fp);\n\tfp = NULL; /* Try again later? */\n\treturn NULL;\n}\n\nint trace_stream_read(struct pid_record_data *pids, int nr_pids, long sleep_us)\n{\n\tstruct pid_record_data *last_pid;\n\tstruct pid_record_data *pid;\n\tstruct tep_record *record;\n\tstruct pollfd pollfd[nr_pids];\n\tlong sleep_ms = sleep_us > 0 ? (sleep_us + 999) / 1000 : sleep_us;\n\tint ret;\n\tint i;\n\n\tif (!nr_pids)\n\t\treturn 0;\n\n\tlast_pid = NULL;\n\n again:\n\tfor (i = 0; i < nr_pids; i++) {\n\t\tpid = &pids[i];\n\n\t\tif (!pid->record)\n\t\t\tpid->record = tracecmd_read_data(pid->instance->handle, pid->cpu);\n\t\trecord = pid->record;\n\t\tif (!record && errno == EINVAL)\n\t\t\t/* pipe has closed */\n\t\t\tpid->closed = 1;\n\n\t\tif (record &&\n\t\t    (!last_pid || record->ts < last_pid->record->ts))\n\t\t\tlast_pid = pid;\n\t}\n\tif (last_pid) {\n\t\ttrace_show_data(last_pid->instance->handle, last_pid->record);\n\t\ttracecmd_free_record(last_pid->record);\n\t\tlast_pid->record = NULL;\n\t\treturn 1;\n\t}\n\n\tfor (i = 0; i < nr_pids; i++) {\n\t\t/* Do not process closed pipes */\n\t\tif (pids[i].closed) {\n\t\t\tmemset(pollfd + i, 0, sizeof(*pollfd));\n\t\t\tcontinue;\n\t\t}\n\n\t\tpollfd[i].fd = pids[i].brass[0];\n\t\tpollfd[i].events = POLLIN;\n\t}\n\n\tret = poll(pollfd, nr_pids, sleep_ms);\n\tif (ret > 0)\n\t\tgoto again;\n\n\treturn ret;\n}\n"
  },
  {
    "path": "tracecmd/trace-tsync.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2021 Google Inc, Steven Rostedt <rostedt@goodmis.org>\n * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n */\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"trace-local.h\"\n#include \"trace-msg.h\"\n\nstruct trace_mapping {\n\tstruct tep_event\t\t*kvm_entry;\n\tstruct tep_format_field\t\t*vcpu_id;\n\tstruct tep_format_field\t\t*common_pid;\n\tint\t\t\t\t*pids;\n\tint\t\t\t\t*map;\n\tint\t\t\t\t*vcpu;\n\tint\t\t\t\tmax_cpus;\n};\n\nstatic int cmp_tmap_vcpu(const void *A, const void *B)\n{\n\tconst int *a = A;\n\tconst int *b = B;\n\n\tif (*a < *b)\n\t\treturn -1;\n\treturn *a > *b;\n}\n\nstatic int map_kvm_vcpus(int guest_pid, struct trace_mapping *tmap)\n{\n\tstruct dirent *entry;\n\tconst char *debugfs;\n\tchar *vm_dir_str = NULL;\n\tchar *pid_file = NULL;\n\tchar *kvm_dir;\n\tint pid_file_len;\n\tbool found = false;\n\tDIR *dir;\n\tint ret = -1;\n\tint i;\n\n\ttmap->vcpu = malloc(sizeof(*tmap->vcpu) * tmap->max_cpus);\n\tif (!tmap->vcpu)\n\t\treturn -1;\n\n\tmemset(tmap->vcpu, -1, sizeof(*tmap->vcpu) * tmap->max_cpus);\n\n\tdebugfs = tracefs_debug_dir();\n\tif (!debugfs)\n\t\treturn -1;\n\n\tif (asprintf(&kvm_dir, \"%s/kvm\", debugfs) < 0)\n\t\treturn -1;\n\n\tdir = opendir(kvm_dir);\n\tif (!dir)\n\t\tgoto out;\n\n\tif (asprintf(&pid_file, \"%d-\", guest_pid) <= 0)\n\t\tgoto out;\n\n\tpid_file_len = strlen(pid_file);\n\n\twhile ((entry = readdir(dir))) {\n\t\tif (entry->d_type != DT_DIR ||\n\t\t    strncmp(entry->d_name, pid_file, pid_file_len) != 0)\n\t\t\tcontinue;\n\t\tif (asprintf(&vm_dir_str, \"%s/%s\", kvm_dir, entry->d_name) < 0)\n\t\t\tgoto out;\n\t\tfound = true;\n\t\tbreak;\n\t}\n\tif (!found)\n\t\tgoto out;\n\n\tclosedir(dir);\n\tdir = opendir(vm_dir_str);\n\tif (!dir)\n\t\tgoto out;\n\ti = 0;\n\twhile ((entry = readdir(dir))) {\n\t\tif (entry->d_type != DT_DIR ||\n\t\t    strncmp(entry->d_name, \"vcpu\", 4))\n\t\t\tcontinue;\n\t\tif (i == tmap->max_cpus)\n\t\t\tgoto out;\n\t\ttmap->vcpu[i] = strtol(entry->d_name + 4, NULL, 10);\n\t\ti++;\n\t}\n\n\tif (i < tmap->max_cpus)\n\t\tgoto out;\n\n\tqsort(tmap->vcpu, tmap->max_cpus, sizeof(*tmap->vcpu), cmp_tmap_vcpu);\n\n\tret = 0;\n\n out:\n\tif (dir)\n\t\tclosedir(dir);\n\tfree(vm_dir_str);\n\tfree(pid_file);\n\tfree(kvm_dir);\n\n\treturn ret;\n}\n\nstatic int map_vcpus(struct tep_event *event, struct tep_record *record,\n\t\t     int cpu, void *context)\n{\n\tstruct trace_mapping *tmap = context;\n\tunsigned long long val;\n\tint *vcpu;\n\tint type;\n\tint pid;\n\tint ret;\n\tint i;\n\n\t/* Do we have junk in the buffer? */\n\ttype = tep_data_type(event->tep, record);\n\tif (type != tmap->kvm_entry->id)\n\t\treturn 0;\n\n\tret = tep_read_number_field(tmap->common_pid, record->data, &val);\n\tif (ret < 0)\n\t\treturn 0;\n\tpid = (int)val;\n\n\tfor (i = 0; tmap->pids[i] >= 0; i++) {\n\t\tif (pid == tmap->pids[i])\n\t\t\tbreak;\n\t}\n\t/* Is this thread one we care about ? */\n\tif (tmap->pids[i] < 0)\n\t\treturn 0;\n\n\tret = tep_read_number_field(tmap->vcpu_id, record->data, &val);\n\tif (ret < 0)\n\t\treturn 0;\n\n\tcpu = (int)val;\n\n\tvcpu = bsearch(&cpu, tmap->vcpu, tmap->max_cpus, sizeof(cpu), cmp_tmap_vcpu);\n\t/* Sanity check, warn? */\n\tif (!vcpu)\n\t\treturn 0;\n\n\tcpu = vcpu - tmap->vcpu;\n\n\t/* Already have this one? Should we check if it is the same? */\n\tif (tmap->map[cpu] >= 0)\n\t\treturn 0;\n\n\ttmap->map[cpu] = pid;\n\n\t/* Did we get them all */\n\tfor (i = 0; i < tmap->max_cpus; i++) {\n\t\tif (tmap->map[i] < 0)\n\t\t\tbreak;\n\t}\n\n\treturn i == tmap->max_cpus;\n}\n\nstatic void start_mapping_vcpus(struct trace_guest *guest)\n{\n\tchar *pids = NULL;\n\tchar *t;\n\tint len = 0;\n\tint s;\n\tint i;\n\n\tif (!guest->task_pids)\n\t\treturn;\n\n\tguest->instance = tracefs_instance_create(\"map_guest_pids\");\n\tif (!guest->instance)\n\t\treturn;\n\n\tfor (i = 0; guest->task_pids[i] >= 0; i++) {\n\t\ts = snprintf(NULL, 0, \"%d \", guest->task_pids[i]);\n\t\tt = realloc(pids, len + s + 1);\n\t\tif (!t) {\n\t\t\tfree(pids);\n\t\t\tpids = NULL;\n\t\t\tbreak;\n\t\t}\n\t\tpids = t;\n\t\tsprintf(pids + len, \"%d \", guest->task_pids[i]);\n\t\tlen += s;\n\t}\n\tif (pids) {\n\t\ttracefs_instance_file_write(guest->instance, \"set_event_pid\", pids);\n\t\tfree(pids);\n\t}\n\ttracefs_instance_file_write(guest->instance, \"events/kvm/kvm_entry/enable\", \"1\");\n}\n\nstatic void stop_mapping_vcpus(int cpu_count, struct trace_guest *guest)\n{\n\tstruct trace_mapping tmap = { };\n\tstruct tep_handle *tep;\n\tconst char *systems[] = { \"kvm\", NULL };\n\tint i;\n\n\tif (!guest->instance)\n\t\treturn;\n\n\ttmap.pids = guest->task_pids;\n\ttmap.max_cpus = cpu_count;\n\n\ttmap.map = malloc(sizeof(*tmap.map) * tmap.max_cpus);\n\tif (!tmap.map)\n\t\treturn;\n\n\t/* Check if the kvm vcpu mappings are the same */\n\tif (map_kvm_vcpus(guest->pid, &tmap) < 0)\n\t\tgoto out;\n\n\tfor (i = 0; i < tmap.max_cpus; i++)\n\t\ttmap.map[i] = -1;\n\n\ttracefs_instance_file_write(guest->instance, \"events/kvm/kvm_entry/enable\", \"0\");\n\n\ttep = tracefs_local_events_system(NULL, systems);\n\tif (!tep)\n\t\tgoto out;\n\n\ttmap.kvm_entry = tep_find_event_by_name(tep, \"kvm\", \"kvm_entry\");\n\tif (!tmap.kvm_entry)\n\t\tgoto out_free;\n\n\ttmap.vcpu_id = tep_find_field(tmap.kvm_entry, \"vcpu_id\");\n\tif (!tmap.vcpu_id)\n\t\tgoto out_free;\n\n\ttmap.common_pid = tep_find_any_field(tmap.kvm_entry, \"common_pid\");\n\tif (!tmap.common_pid)\n\t\tgoto out_free;\n\n\ttracefs_iterate_raw_events(tep, guest->instance, NULL, 0, map_vcpus, &tmap);\n\n\tfor (i = 0; i < tmap.max_cpus; i++) {\n\t\tif (tmap.map[i] < 0)\n\t\t\tbreak;\n\t}\n\t/* We found all the mapped CPUs */\n\tif (i == tmap.max_cpus) {\n\t\tguest->cpu_pid = tmap.map;\n\t\tguest->cpu_max = tmap.max_cpus;\n\t\ttmap.map = NULL;\n\t}\n\n out_free:\n\ttep_free(tep);\n out:\n\tfree(tmap.vcpu);\n\tfree(tmap.map);\n\ttracefs_instance_destroy(guest->instance);\n\ttracefs_instance_free(guest->instance);\n}\n\n/**\n * trace_tsync_as_host - tsync from the host side\n * @fd: The descriptor to the peer for tsync\n * @trace_id: The trace_id of the host\n * @loop_interval: The loop interval for tsyncs that do periodic syncs\n * @guest_id: The id for guests (negative if this is over network)\n * @guest_cpus: The number of CPUs the guest has\n * @proto_name: The protocol name to sync with\n * @clock: The clock name to use for tracing\n *\n * Start the time synchronization from the host side.\n * This will start the mapping of the virtual CPUs to host threads\n * if it is a vsocket connection (not a network).\n *\n * Returns a pointer to the tsync descriptor on success or NULL on error.\n */\nstruct tracecmd_time_sync *\ntrace_tsync_as_host(int fd, unsigned long long trace_id,\n\t\t    int loop_interval, int guest_id,\n\t\t    int guest_cpus, const char *proto_name,\n\t\t    const char *clock)\n{\n\tstruct tracecmd_time_sync *tsync;\n\tstruct trace_guest *guest;\n\tint guest_pid = -1;\n\n\tif (fd < 0)\n\t\treturn NULL;\n\n\tif (guest_id >= 0) {\n\t\tguest = trace_get_guest(guest_id, NULL);\n\t\tif (guest == NULL)\n\t\t\treturn NULL;\n\t\tguest_pid = guest->pid;\n\t\tstart_mapping_vcpus(guest);\n\t}\n\n\ttsync = tracecmd_tsync_with_guest(trace_id, loop_interval, fd,\n\t\t\t\t\t  guest_pid, guest_cpus, proto_name,\n\t\t\t\t\t  clock);\n\n\tif (guest_id >= 0)\n\t\tstop_mapping_vcpus(guest_cpus, guest);\n\n\treturn tsync;\n}\n\n/**\n * trace_tsync_a_guest - tsync from the guest side\n * @fd: The file descriptor to the peer for tsync\n * @tsync_proto: The protocol name to sync with\n * @clock: The clock name to use for tracing\n * @remote_id: The id to differentiate the remote server with\n * @loca_id: The id to differentiate the local machine with\n *\n * Start the time synchronization from the guest side.\n *\n * Returns a pointer to the tsync descriptor on success or NULL on error.\n */\nstruct tracecmd_time_sync *\ntrace_tsync_as_guest(int fd, const char *tsync_proto, const char *clock,\n\t       unsigned int remote_id, unsigned int local_id)\n{\n\tstruct tracecmd_time_sync *tsync = NULL;\n\n\tif (fd < 0)\n\t\t return NULL;\n\n\ttsync = tracecmd_tsync_with_host(fd, tsync_proto,\n\t\t\t\t\t clock, remote_id, local_id);\n\tif (!tsync) {\n\t\twarning(\"Failed to negotiate timestamps synchronization with the host\");\n\t\treturn NULL;\n\t}\n\n\treturn tsync;\n}\n"
  },
  {
    "path": "tracecmd/trace-usage.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <libgen.h>\n\n#include \"trace-local.h\"\n#include \"version.h\"\n\nstruct usage_help {\n\tchar *name;\n\tchar *short_help;\n\tchar *long_help;\n};\n\nstatic struct usage_help usage_help[] = {\n\t{\n\t\t\"record\",\n\t\t\"record a trace into a trace.dat file\",\n\t\t\" %s record [-v][-e event [-f filter]][-p plugin][-F][-d][-D][-o file] \\\\\\n\"\n\t\t\"           [-q][-s usecs][-O option ][-l func][-g func][-n func] \\\\\\n\"\n\t\t\"           [-P pid][-N host:port][-t][-r prio][-b size][-B buf] \\\\\\n\"\n\t\t\"           [--proxy vsock][command ...]\\n\"\n\t\t\"           [-m max][-C clock]\\n\"\n\t\t\"          -e run command with event enabled\\n\"\n\t\t\"          -f filter for previous -e event\\n\"\n\t\t\"          -R trigger for previous -e event\\n\"\n\t\t\"          -p run command with plugin enabled\\n\"\n\t\t\"          -F filter only on the given process\\n\"\n\t\t\"          -P trace the given pid like -F for the command\\n\"\n\t\t\"          -c also trace the children of -F (or -P if kernel supports it)\\n\"\n\t\t\"          -C set the trace clock\\n\"\n\t\t\"          -T do a stacktrace on all events\\n\"\n\t\t\"          -l filter function name\\n\"\n\t\t\"          -g set graph function\\n\"\n\t\t\"          -n do not trace function\\n\"\n\t\t\"          -m max size per CPU in kilobytes\\n\"\n\t\t\"          -M set CPU mask to trace\\n\"\n\t\t\"          -v will negate all -e (disable those events) and -B (delete those instances) after it\\n\"\n\t\t\"          -d disable function tracer when running\\n\"\n\t\t\"          -D Full disable of function tracing (for all users)\\n\"\n\t\t\"          -o data output file [default trace.dat]\\n\"\n\t\t\"          -O option to enable (or disable)\\n\"\n\t\t\"          -r real time priority to run the capture threads\\n\"\n\t\t\"          -s sleep interval between recording (in usecs) [default: 1000]\\n\"\n\t\t\"          -S used with --profile, to enable only events in command line\\n\"\n\t\t\"          -N host:port to connect to (see listen)\\n\"\n\t\t\"          -V cid:port to connect to via vsocket (see listen)\\n\"\n\t\t\"          -t used with -N, forces use of tcp in live trace\\n\"\n\t\t\"          -b change kernel buffersize (in kilobytes per CPU)\\n\"\n\t\t\"          -B create sub buffer and following events will be enabled here\\n\"\n\t\t\"          -k do not reset the buffers after tracing.\\n\"\n\t\t\"          -K do not reset the buffers before tracing.\\n\"\n\t\t\"          -i do not fail if an event is not found\\n\"\n\t\t\"          -q print no output to the screen\\n\"\n\t\t\"          -G when profiling, set soft and hard irqs as global\\n\"\n\t\t\"          --quiet print no output to the screen\\n\"\n\t\t\"          --temp specify a directory to store the temp files used to create trace.dat\\n\"\n\t\t\"          --subbuf-size to specify the sub-buffer size in kilobytes\\n\"\n\t\t\"          --module filter module name\\n\"\n\t\t\"          --by-comm used with --profile, merge events for related comms\\n\"\n\t\t\"          --profile enable tracing options needed for report --profile\\n\"\n\t\t\"          --func-stack perform a stack trace for function tracer\\n\"\n\t\t\"             (use with caution)\\n\"\n\t\t\"          --max-graph-depth limit function_graph depth\\n\"\n\t\t\"          --cmdlines-size change kernel saved_cmdlines_size\\n\"\n\t\t\"          --no-filter include trace-cmd threads in the trace\\n\"\n\t\t\"          --proc-map save the traced processes address map into the trace.dat file\\n\"\n\t\t\"          --user execute the specified [command ...] as given user\\n\"\n\t\t\"          --tsc2nsec Convert the current clock to nanoseconds, using tsc multiplier and shift from the Linux\"\n\t\t\"               kernel's perf interface\\n\"\n\t\t\"          --tsync-interval set the loop interval, in ms, for timestamps synchronization with guests:\"\n\t\t\"               If a negative number is specified, timestamps synchronization is disabled\"\n\t\t\"               If 0 is specified, no loop is performed - timestamps offset is calculated only twice,\"\n\t\t\"                                                         at the beginnig and at the end of the trace\\n\"\n\t\t\"          --poll don't block while reading from the trace buffer\\n\"\n\t\t\"          --name used with -A to give the agent a specific name\\n\"\n\t\t\"          --file-version set the desired trace file version\\n\"\n\t\t\"          --compression compress the trace output file, one of these strings can be passed:\\n\"\n\t\t\"                            any  - auto select the best available compression algorithm\\n\"\n\t\t\"                            none - do not compress the trace file\\n\"\n\t\t\"                            name - the name of the desired compression algorithms\\n\"\n\t\t\"                        available algorithms can be listed with trace-cmd list -c\\n\"\n\t\t\"          --proxy vsocket to reach the agent. Acts the same as -A (for an agent)\\n\"\n\t\t\"              but will send the proxy connection to the agent.\\n\"\n\t\t\"          --daemonize run trace-cmd in the background as a daemon after recording has started.\\n\"\n\t\t\"                      creates a pidfile at /var/run/trace-cmd-record.pid with the pid of trace-cmd\\n\"\n\t\t\"                      during the recording.\\n\"\n\t},\n\t{\n\t\t\"set\",\n\t\t\"set a ftrace configuration parameter\",\n\t\t\" %s set [-v][-e event [-f filter]][-p plugin][-F][-d][-D] \\\\\\n\"\n\t\t\"           [-q][-s usecs][-O option ][-l func][-g func][-n func] \\\\\\n\"\n\t\t\"           [-P pid][-b size][-B buf][-m max][-C clock][command ...]\\n\"\n\t\t\"          -e enable event\\n\"\n\t\t\"          -f filter for previous -e event\\n\"\n\t\t\"          -R trigger for previous -e event\\n\"\n\t\t\"          -p set ftrace plugin\\n\"\n\t\t\"          -P set PIDs to be traced\\n\"\n\t\t\"          -c also trace the children of -F (or -P if kernel supports it)\\n\"\n\t\t\"          -C set the trace clock\\n\"\n\t\t\"          -T do a stacktrace on all events\\n\"\n\t\t\"          -l filter function name\\n\"\n\t\t\"          -g set graph function\\n\"\n\t\t\"          -n do not trace function\\n\"\n\t\t\"          -m max size per CPU in kilobytes\\n\"\n\t\t\"          -M set CPU mask to trace\\n\"\n\t\t\"          -v will negate all -e (disable those events) and -B (delete those instances) after it\\n\"\n\t\t\"          -d disable function tracer when running\\n\"\n\t\t\"          -D Full disable of function tracing (for all users)\\n\"\n\t\t\"          -O option to enable (or disable)\\n\"\n\t\t\"          -b change kernel buffersize (in kilobytes per CPU)\\n\"\n\t\t\"          -B create sub buffer and following events will be enabled here\\n\"\n\t\t\"          -i do not fail if an event is not found\\n\"\n\t\t\"          -q print no output to the screen\\n\"\n\t\t\"          --quiet print no output to the screen\\n\"\n\t\t\"          --module filter module name\\n\"\n\t\t\"          --func-stack perform a stack trace for function tracer\\n\"\n\t\t\"             (use with caution)\\n\"\n\t\t\"          --max-graph-depth limit function_graph depth\\n\"\n\t\t\"          --cmdlines-size change kernel saved_cmdlines_size\\n\"\n\t\t\"          --user execute the specified [command ...] as given user\\n\"\n\t\t\"          --fork return immediately if a command is specified\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"start\",\n\t\t\"start tracing without recording into a file\",\n\t\t\" %s start [-e event][-p plugin][-d][-O option ][-P pid]\\n\"\n\t\t\"          Uses same options as record.\\n\"\n\t\t\"          It only enables the tracing and exits\\n\"\n\t\t\"\\n\"\n\t\t\"        --fork: If a command is specified, then return right after it forks\\n\"\n\t\t\"        --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"extract\",\n\t\t\"extract a trace from the kernel\",\n\t\t\" %s extract [-p plugin][-O option][-o file][-B buf][-s][-a][-t]\\n\"\n\t\t\"          Uses similar options as record, but only reads an existing trace.\\n\"\n\t\t\"          -s : extract the snapshot instead of the main buffer\\n\"\n\t\t\"          -B : extract a given buffer (more than one may be specified)\\n\"\n\t\t\"          -a : extract all buffers (except top one)\\n\"\n\t\t\"          -t : extract the top level buffer (useful with -B and -a)\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"stop\",\n\t\t\"stop the kernel from recording trace data\",\n\t\t\" %s stop [-B buf [-B buf]..] [-a] [-t]\\n\"\n\t\t\"          Stops the tracer from recording more data.\\n\"\n\t\t\"          Used in conjunction with start\\n\"\n\t\t\"          -B stop a given buffer (more than one may be specified)\\n\"\n\t\t\"          -a stop all buffers (except top one)\\n\"\n\t\t\"          -t stop the top level buffer (useful with -B or -a)\\n\"\n\t},\n\t{\n\t\t\"restart\",\n\t\t\"restart the kernel trace data recording\",\n\t\t\" %s restart [-B buf [-B buf]..] [-a] [-t]\\n\"\n\t\t\"          Restarts recording after a trace-cmd stop.\\n\"\n\t\t\"          Used in conjunction with stop\\n\"\n\t\t\"          -B restart a given buffer (more than one may be specified)\\n\"\n\t\t\"          -a restart all buffers (except top one)\\n\"\n\t\t\"          -t restart the top level buffer (useful with -B or -a)\\n\"\n\t},\n\t{\n\t\t\"show\",\n\t\t\"show the contents of the kernel tracing buffer\",\n\t\t\" %s show [-p|-s][-c cpu][-B buf][options]\\n\"\n\t\t\"          Basically, this is a cat of the trace file.\\n\"\n\t\t\"          -p read the trace_pipe file instead\\n\"\n\t\t\"          -s read the snapshot file instance\\n\"\n\t\t\"           (Can't have both -p and -s)\\n\"\n\t\t\"          -c just show the file associated with a given CPU\\n\"\n\t\t\"          -B read from a tracing buffer instance.\\n\"\n\t\t\"          -f display the file path that is being dumped\\n\"\n\t\t\"          The following options shows the corresponding file name\\n\"\n\t\t\"           and then exits.\\n\"\n\t\t\"          --hist event (show the hist file of an event)\\n\"\n\t\t\"          --trigger event (show the trigger file of an event)\\n\"\n\t\t\"          --tracing_on\\n\"\n\t\t\"          --current_tracer\\n\"\n\t\t\"          --buffer_size (for buffer_size_kb)\\n\"\n\t\t\"          --buffer_total_size (for buffer_total_size_kb)\\n\"\n\t\t\"          --buffer_subbuf_size (for buffer_subbuf_size_kb)\\n\"\n\t\t\"          --buffer_percent (for buffer_percent)\\n\"\n\t\t\"          --ftrace_filter (for set_ftrace_filter)\\n\"\n\t\t\"          --ftrace_notrace (for set_ftrace_notrace)\\n\"\n\t\t\"          --ftrace_pid (for set_ftrace_pid)\\n\"\n\t\t\"          --graph_function (for set_graph_function)\\n\"\n\t\t\"          --graph_notrace (for set_graph_notrace)\\n\"\n\t\t\"          --cpumask (for tracing_cpumask)\\n\"\n\t\t\"          --max_latency (for tracing_max_latency)\\n\"\n\t},\n\t{\n\t\t\"reset\",\n\t\t\"disable all kernel tracing and clear the trace buffers\",\n\t\t\" %s reset [-b size][-B buf][-k event][-a][-d][-t]\\n\"\n\t\t\"          Disables the tracer (may reset trace file)\\n\"\n\t\t\"          Used in conjunction with start\\n\"\n\t\t\"          -b change the kernel buffer size (in kilobytes per CPU)\\n\"\n\t\t\"          -d delete the previous specified instance\\n\"\n\t\t\"          -B reset the given buffer instance (may specify multiple -B)\\n\"\n\t\t\"          -a reset all instances (except top one)\\n\"\n\t\t\"          -t reset the top level instance (useful with -B or -a)\\n\"\n\t\t\"          -k keep dynamic event during reset (can be specified multiple times).\\n\"\n\t\t\"              Valid values are:\\n\"\n\t\t\"              'kprobe', 'kretprobe', 'uprobe', 'uretprobe',\\n\"\n\t\t\"              'eprobe', 'synth' and 'all'.\\n\"\n\t},\n\t{\n\t\t\"clear\",\n\t\t\"clear the trace buffers\",\n\t\t\" %s clear [-B buf][-a]\\n\"\n\t\t\"          -B clear the given buffer (may specify multiple -B)\\n\"\n\t\t\"          -a clear all existing buffers, including the top level one\\n\"\n\t},\n\t{\n\t\t\"report\",\n\t\t\"read out the trace stored in a trace.dat file\",\n\t\t\" %s report [-i file] [--cpu cpu] [-e][-f][-l][-P][-L][-N][-R][-E]\\\\\\n\"\n\t\t\"           [-r events][-n events][-F filter][-v][-V[1-6]][-T][-O option]\\n\"\n\t\t\"           [-H [start_system:]start_event,start_match[,pid]/[end_system:]end_event,end_match[,flags]\\n\"\n\t\t\"           [-G]\\n\"\n\t\t\"          -i input file [default trace.dat]\\n\"\n\t\t\"          -e show file endianess\\n\"\n\t\t\"          -f show function mapping list\\n\"\n\t\t\"          -P show printk list\\n\"\n\t\t\"          -E show event files stored\\n\"\n\t\t\"          -F filter to filter output on\\n\"\n\t\t\"          -I filter out events with the HARDIRQ flag set\\n\"\n\t\t\"          -S filter out events with the SOFTIRQ flag set\\n\"\n\t\t\"          -t print out full timestamp. Do not truncate to 6 places.\\n\"\n\t\t\"          -R raw format: ignore print format and only show field data\\n\"\n\t\t\"          -r raw format the events that match the option\\n\"\n\t\t\"          -v will negate all -F after it (Not show matches)\\n\"\n\t\t\"          -T print out the filter strings created and exit\\n\"\n\t\t\"          -V[level] verbose (shows plugins being loaded)\\n\"\n\t\t\"              With optional level (see --verbose numbers)\\n\"\n\t\t\"          -L load only local (~/.trace-cmd/plugins) plugins\\n\"\n\t\t\"          -N do not load any plugins\\n\"\n\t\t\"          -n ignore plugin handlers for events that match the option\\n\"\n\t\t\"          -w show wakeup latencies\\n\"\n\t\t\"          -l show latency format (default with latency tracers)\\n\"\n\t\t\"          -O plugin option -O [plugin:]var[=val]\\n\"\n\t\t\"          --cpu <cpu1,cpu2,...> - filter events according to the given cpu list.\\n\"\n\t\t\"                                  A range of CPUs can be specified using 'cpuX-cpuY' notation.\\n\"\n\t\t\"          --cpus - List the CPUs that have content in it then exit.\\n\"\n\t\t\"          --first-event - Show the timestamp of the first event for all CPUs.\\n\"\n\t\t\"          --last-event - Show the timestamp of the last event for all CPUs.\\n\"\n\t\t\"          --check-events return whether all event formats can be parsed\\n\"\n\t\t\"          --stat - show the buffer stats that were reported at the end of the record.\\n\"\n\t\t\"          --uname - show uname of the record, if it was saved\\n\"\n\t\t\"          --version - show version used to build the trace-cmd exec that created the file\\n\"\n\t\t\"          --profile report stats on where tasks are blocked and such\\n\"\n\t\t\"          -G when profiling, set soft and hard irqs as global\\n\"\n\t\t\"          -H Allows users to hook two events together for timings\\n\"\n\t\t\"             (used with --profile)\\n\"\n\t\t\"          --by-comm used with --profile, merge events for related comms\\n\"\n\t\t\"          --ts-offset will add amount to timestamp of all events of the\\n\"\n\t\t\"                     previous data file.\\n\"\n\t\t\"          --ts2secs HZ, pass in the timestamp frequency (per second)\\n\"\n\t\t\"                     to convert the displayed timestamps to seconds\\n\"\n\t\t\"                     Affects the previous data file, unless there was no\\n\"\n\t\t\"                     previous data file, in which case it becomes default\\n\"\n\t\t\"          --ts-diff Show the delta timestamp between events.\\n\"\n\t\t\"          --ts-check Check to make sure no time stamp on any CPU goes backwards.\\n\"\n\t\t\"          --nodate Ignore the --date processing of trace-cmd record.\\n\"\n\t\t\"          --raw-ts Display raw timestamps, without any corrections.\\n\"\n\t\t\"          --align-ts Display timestamps aligned to the first event.\\n\"\n\t\t\"          --verbose[=level] Set the desired log level\\n\"\n\t\t\"                0 or none  - no error messages\\n\"\n\t\t\"                1 or crit  - only critical messages\\n\"\n\t\t\"                2 or err   - 'crit' and error messages\\n\"\n\t\t\"                3 or warn  - 'err' and warning messages\\n\"\n\t\t\"                4 or info  - 'warn' and informational messages\\n\"\n\t\t\"                5 or debug - 'info' and debugging messages\\n\"\n\t\t\"                6 or all   - same as debug\\n\"\n\t},\n\t{\n\t\t\"stream\",\n\t\t\"Start tracing and read the output directly\",\n\t\t\" %s stream [-e event][-p plugin][-d][-O option ][-P pid]\\n\"\n\t\t\"          Uses same options as record but does not write to files or the network.\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"profile\",\n\t\t\"Start profiling and read the output directly\",\n\t\t\" %s profile [-e event][-p plugin][-d][-O option ][-P pid][-G][-S][-o output]\\n\"\n\t\t\"    [-H [start_system:]start_event,start_match[,pid]/[end_system:]end_event,end_match[,flags]\\n\\n\"\n\t\t\"          Uses same options as record --profile.\\n\"\n\t\t\"          -H Allows users to hook two events together for timings\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"hist\",\n\t\t\"show a histogram of the trace.dat information\",\n\t\t\" %s hist [-i file][-P] [file]\"\n\t\t\"          -P ignore pids (compact all functions)\\n\"\n\t},\n\t{\n\t\t\"stat\",\n\t\t\"show the status of the running tracing (ftrace) system\",\n\t\t\" %s stat [-B buf][-t][-o]\"\n\t\t\"          -B show the status of a instance buffer\\n\"\n\t\t\"          -t show the top level status along with buffer specified by -B\\n\"\n\t\t\"          -o list tracing options\\n\"\n\t},\n\t{\n\t\t\"split\",\n\t\t\"parse a trace.dat file into smaller file(s)\",\n\t\t\" %s split [options] -o file [start [end]]\\n\"\n\t\t\"          -o output file to write to (file.1, file.2, etc)\\n\"\n\t\t\"          -s n  split file up by n seconds\\n\"\n\t\t\"          -m n  split file up by n milliseconds\\n\"\n\t\t\"          -u n  split file up by n microseconds\\n\"\n\t\t\"          -e n  split file up by n events\\n\"\n\t\t\"          -p n  split file up by n pages\\n\"\n\t\t\"          -C n  select CPU n\\n\"\n\t\t\"          -B buffer  keep buffer in resulting .dat file\\n\"\n\t\t\"                     Use -t to promote the buffer to the top instance.\\n\"\n\t\t\"          -t    promote preceding buffer to the top instance.\\n\"\n\t\t\"                Must follow -B.\\n\"\n\t\t\"          --top keep top buffer in resulting .dat file.\\n\"\n\t\t\"          -b    new name of the top instance. Must follow --top.\\n\"\n\t\t\"          -r    repeat from start to end\\n\"\n\t\t\"          -c    per cpu, that is -p 2 will be 2 pages for each CPU\\n\"\n\t\t\"          if option is specified, it will split the file\\n\"\n\t\t\"           up starting at start, and ending at end\\n\"\n\t\t\"          start - decimal start time in seconds (ex: 75678.923853)\\n\"\n\t\t\"                  if left out, will start at beginning of file\\n\"\n\t\t\"          end   - decimal end time in seconds\\n\"\n\t},\n\t{\n\t\t\"options\",\n\t\t\"list the plugin options available for trace-cmd report\",\n\t\t\" %s options\\n\"\n\t},\n\t{\n\t\t\"listen\",\n\t\t\"listen on a network socket for trace clients\",\n\t\t\" %s listen -p port[-D][-o file][-d dir][-l logfile]\\n\"\n\t\t\"          Creates a socket to listen for clients.\\n\"\n\t\t\"          -p port number to listen on.\\n\"\n\t\t\"          -D run in daemon mode.\\n\"\n\t\t\"          -V listen on a vsocket instead.\\n\"\n\t\t\"          -o file name to use for clients.\\n\"\n\t\t\"          -d directory to store client files.\\n\"\n\t\t\"          -l logfile to write messages to.\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"agent\",\n\t\t\"listen on a vsocket for trace clients\",\n\t\t\" %s agent -p port[-D][-N IP][-P cid]\\n\"\n\t\t\"          Creates a vsocket to listen for clients.\\n\"\n\t\t\"          -N Connect to IP via TCP instead of vsockets\\n\"\n\t\t\"             *** Insecure setting, only use on a trusted network ***\\n\"\n\t\t\"             ***   Only use if the client is totally trusted.    ***\\n\"\n\t\t\"          -p port number to listen on.\\n\"\n\t\t\"          -D run in daemon mode.\\n\"\n\t\t\"          -P Also act as a proxy server, with a single client denoted\\n\"\n\t\t\"             by a context ID (cid).\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"setup-guest\",\n\t\t\"create FIFOs for tracing guest VMs\",\n\t\t\" %s setup-guest [-c cpus][-p perm][-g group][-a] guest\\n\"\n\t\t\"          -c number of guest virtual CPUs\\n\"\n\t\t\"          -p FIFOs permissions (default: 0660)\\n\"\n\t\t\"          -g FIFOs group owner\\n\"\n\t\t\"          -a Attach FIFOs to guest VM config\\n\"\n\t},\n\t{\n\t\t\"list\",\n\t\t\"list the available events, plugins or options\",\n\t\t\" %s list [-e [regex]][-t][-o][-f [regex]]\\n\"\n\t\t\"          -e list available events\\n\"\n\t\t\"            -F show event format\\n\"\n\t\t\"            --full show the print fmt with -F\\n\"\n\t\t\"            -R show event triggers\\n\"\n\t\t\"            -l show event filters\\n\"\n\t\t\"          -t list available tracers\\n\"\n\t\t\"          -o list available options\\n\"\n\t\t\"          -f [regex] list available functions to filter on\\n\"\n\t\t\"          -P list loaded plugin files (by path)\\n\"\n\t\t\"          -O list plugin options\\n\"\n\t\t\"          -B list defined buffer instances\\n\"\n\t\t\"          -C list the defined clocks (and active one)\\n\"\n\t\t\"          -c list the supported trace file compression algorithms\\n\"\n\t\t\"          --proto list the prototype of a function (if BTF is available)\\n\"\n\t},\n\t{\n\t\t\"restore\",\n\t\t\"restore a crashed record\",\n\t\t\" %s restore [-c][-o file][-i file] cpu-file [cpu-file ...]\\n\"\n\t\t\"          -c create a partial trace.dat file only\\n\"\n\t\t\"          -o output file\\n\"\n\t\t\"          -i partial trace.dat file for input\\n\"\n\t},\n\t{\n\t\t\"snapshot\",\n\t\t\"take snapshot of running trace\",\n\t\t\" %s snapshot [-s][-r][-f][-B buf][-c cpu]\\n\"\n\t\t\"          -s take a snapshot of the trace buffer\\n\"\n\t\t\"          -r reset current snapshot\\n\"\n\t\t\"          -f free the snapshot buffer\\n\"\n\t\t\"            without the above three options, display snapshot\\n\"\n\t\t\"          -c operate on the snapshot buffer for the given CPU\\n\"\n\t\t\"          -B operate on the snapshot buffer for a tracing buffer instance.\\n\"\n\t},\n\t{\n\t\t\"stack\",\n\t\t\"output, enable or disable kernel stack tracing\",\n\t\t\" %s stack [--start][--stop][--reset]\\n\"\n\t\t\"          --start  enable the stack tracer\\n\"\n\t\t\"          --stop   disable the stack tracer\\n\"\n\t\t\"          --reset  reset the maximum stack found\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"check-events\",\n\t\t\"parse trace event formats\",\n\t\t\" %s check-events [-N]\\n\"\n\t\t\"          -N do not load any plugins\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"dump\",\n\t\t\"read out the meta data from a trace file\",\n\t\t\" %s dump [options]\\n\"\n\t\t\"          -i input file, default is trace.dat\\n\"\n\t\t\"          -v validate a trace file\\n\"\n\t\t\"          --all print all meta data from a trace file\\n\"\n\t\t\"          --summary print a meta data summary\\n\"\n\t\t\"          --head-page print header page information\\n\"\n\t\t\"          --head-event print header event information\\n\"\n\t\t\"          --ftrace-events print ftrace events format\\n\"\n\t\t\"          --systems print recorded event systems\\n\"\n\t\t\"          --events print format of recorded events\\n\"\n\t\t\"          --kallsyms print information of the mapping of function addresses to the function names\\n\"\n\t\t\"          --printk print trace_printk() format strings\\n\"\n\t\t\"          --cmd-lines print information mapping a PID to a process name\\n\"\n\t\t\"          --options print options\\n\"\n\t\t\"          --flyrecord information of offset and count of recorded events per CPU\\n\"\n\t\t\"          --clock trace clock, saved in the file\\n\"\n\t\t\"          -h, --help show usage information\\n\"\n\t\t\"          --verbose 'level' Set the desired log level\\n\"\n\t},\n\t{\n\t\t\"attach\",\n\t\t\"Attach a host and guest trace.dat file\",\n\t\t\" %s attach [options] host_file guest_file vcpu_pid,...\\n\"\n\t\t\"          -s  offset,scale,fraction[,timestamp] conversion to sync guest timestamp\\n\"\n\t\t\"          host_file The trace.dat file from the host\\n\"\n\t\t\"          guest_file The trace.dat file from the guest\\n\"\n\t\t\"          vcpu_pid list of process ids from the host that represent the vCPUs of the guest\\n\"\n\t},\n\t{\n\t\t\"convert\",\n\t\t\"convert trace file to different version\",\n\t\t\" %s convert [options]\\n\"\n\t\t\"          -i input file, default is trace.dat\\n\"\n\t\t\"          -o output file, mandatory parameter.\\n\"\n\t\t\"             The output file can be specified also as last argument of the command\\n\"\n\t\t\"          --file-version set the desired trace file version\\n\"\n\t\t\"          --compression compress the trace output file, one of these strings can be passed:\\n\"\n\t\t\"                            any  - auto select the best available compression algorithm\\n\"\n\t\t\"                            none - do not compress the trace file\\n\"\n\t\t\"                            name - the name of the desired compression algorithms\\n\"\n\t\t\"                        available algorithms can be listed with trace-cmd list -c\\n\"\n\t},\n\t{\n\t\t\"sqlhist\",\n\t\t\"Run a SQL like query to create histogram or synthetic events (see man tracefs_sql(3))\\n\",\n\t\t\"%s sql [-n name][-e][-s][-S fields][-m var][-c var][-T][-t dir][-f file | 'sql-command-line']\\n\"\n\t\t\"  -n name - name of synthetic event 'Anonymous' if left off\\n\"\n\t\t\"  -t dir - use dir instead of /sys/kernel/tracing\\n\"\n\t\t\"  -e - execute the commands to create the synthetic event\\n\"\n\t\t\"  -m - trigger the action when var is a new max.\\n\"\n\t\t\"  -c - trigger the action when var changes.\\n\"\n\t\t\"  -s - used with -m or -c to do a snapshot of the tracing buffer\\n\"\n\t\t\"  -S - used with -m or -c to save fields of the end event (comma deliminated)\\n\"\n\t\t\"  -T - used with -m or -c to do both a snapshot and a trace\\n\"\n\t\t\"  -f file - read sql lines from file otherwise from the command line\\n\"\n\t\t\"\t    if file is '-' then read from standard input.\\n\\n\"\n\t\t\" See man tracefs_sql(3) for sql-command-line\\n\"\n\t},\n\n\t{\n\t\tNULL, NULL, NULL\n\t}\n};\n\nstatic struct usage_help *find_help(char *cmd)\n{\n\tstruct usage_help *help;\n\n\thelp = usage_help;\n\twhile (help->name) {\n\t\tif (strcmp(cmd, help->name) == 0)\n\t\t\treturn help;\n\t\thelp++;\n\t}\n\treturn NULL;\n}\n\nvoid usage(char **argv)\n{\n\tstruct usage_help *help = NULL;\n\tchar *arg = argv[0];\n\tchar *p;\n\n\tp = basename(arg);\n\n\tprintf(\"\\n\"\n\t       \"%s version %s (%s)\\n\\n\"\n\t       \"usage:\\n\", p, VERSION_STRING, VERSION_GIT);\n\n\tif (argv[1])\n\t\thelp = find_help(argv[1]);\n\n\tif (help) {\n\t\tprintf(help->long_help, p);\n\t\tgoto out;\n\t}\n\n\tprintf(\"  %s [COMMAND] ...\\n\\n\"\n\t       \"  commands:\\n\", p);\n\n\thelp = usage_help;\n\twhile (help->name) {\n\t\tprintf(\"     %s - %s\\n\", help->name, help->short_help);\n\t\thelp++;\n\t}\n out:\n\tprintf(\"\\n\");\n\texit(-1);\n}\n\n\nvoid trace_usage(int argc, char **argv)\n{\n\tusage(argv);\n}\n"
  },
  {
    "path": "tracecmd/trace-vm.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>\n * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <dirent.h>\n#include <limits.h>\n#include <unistd.h>\n#include <errno.h>\n\n#include \"trace-local.h\"\n#include \"trace-msg.h\"\n\nstatic struct trace_guest *guests;\nstatic size_t guests_len;\n\nstatic struct trace_guest *get_guest_by_cid(unsigned int guest_cid)\n{\n\tint i;\n\n\tif (!guests)\n\t\treturn NULL;\n\n\tfor (i = 0; i < guests_len; i++)\n\t\tif (guest_cid == guests[i].cid)\n\t\t\treturn guests + i;\n\treturn NULL;\n}\n\nstatic struct trace_guest *get_guest_by_name(const char *name)\n{\n\tint i;\n\n\tif (!guests || !strlen(name))\n\t\treturn NULL;\n\n\tfor (i = 0; i < guests_len; i++)\n\t\tif (strcmp(name, guests[i].name) == 0)\n\t\t\treturn guests + i;\n\treturn NULL;\n}\n\nbool trace_have_guests_pid(void)\n{\n\tfor (int i = 0; i < guests_len; i++) {\n\t\tif (guests[i].pid < 0)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/* Find all the tasks associated with the guest pid */\nstatic void find_tasks(struct trace_guest *guest)\n{\n\tstruct dirent *dent;\n\tchar *path;\n\tDIR *dir;\n\tint ret;\n\tint tasks = 0;\n\n\tret = asprintf(&path, \"/proc/%d/task\", guest->pid);\n\tif (ret < 0)\n\t\treturn;\n\n\tdir = opendir(path);\n\tfree(path);\n\tif (!dir)\n\t\treturn;\n\n\twhile ((dent = readdir(dir))) {\n\t\tint *pids;\n\t\tif (!(dent->d_type == DT_DIR && is_digits(dent->d_name)))\n\t\t\tcontinue;\n\t\tpids = realloc(guest->task_pids, sizeof(int) * (tasks + 2));\n\t\tif (!pids)\n\t\t\tbreak;\n\t\tpids[tasks++] = strtol(dent->d_name, NULL, 0);\n\t\tpids[tasks] = -1;\n\t\tguest->task_pids = pids;\n\t}\n\tclosedir(dir);\n}\n\nstatic void find_pid_by_cid(struct trace_guest *guest);\n\nstatic struct trace_guest *add_guest(unsigned int cid, const char *name)\n{\n\tstruct trace_guest *guest;\n\n\tguests = realloc(guests, (guests_len + 1) * sizeof(*guests));\n\tif (!guests)\n\t\tdie(\"allocating new guest\");\n\n\tguest = &guests[guests_len++];\n\n\tmemset(guest, 0, sizeof(*guest));\n\tguest->name = strdup(name);\n\tif (!guest->name)\n\t\tdie(\"allocating guest name\");\n\tguest->cid = cid;\n\tguest->pid = -1;\n\n\tfind_pid_by_cid(guest);\n\tfind_tasks(guest);\n\n\treturn guest;\n}\n\nstatic struct tracefs_instance *start_trace_connect(void)\n{\n\tstruct tracefs_instance *open_instance;\n\n\topen_instance = tracefs_instance_create(\"vsock_find_pid\");\n\tif (!open_instance)\n\t\treturn NULL;\n\n\ttracefs_event_enable(open_instance, \"sched\", \"sched_waking\");\n\ttracefs_event_enable(open_instance, \"kvm\", \"kvm_exit\");\n\ttracefs_trace_on(open_instance);\n\treturn open_instance;\n}\n\nstruct pids {\n\tstruct pids\t\t*next;\n\tint\t\t\tpid;\n};\n\nstruct trace_fields {\n\tstruct tep_event\t\t*sched_waking;\n\tstruct tep_event\t\t*kvm_exit;\n\tstruct tep_format_field\t\t*common_pid;\n\tstruct tep_format_field\t\t*sched_next;\n\tstruct pids\t\t\t*pids;\n\tint\t\t\t\tfound_pid;\n};\n\nstatic void free_pids(struct pids *pids)\n{\n\tstruct pids *next;\n\n\twhile (pids) {\n\t\tnext = pids;\n\t\tpids = pids->next;\n\t\tfree(next);\n\t}\n}\n\nstatic void add_pid(struct pids **pids, int pid)\n{\n\tstruct pids *new_pid;\n\n\tnew_pid = malloc(sizeof(*new_pid));\n\tif (!new_pid)\n\t\treturn;\n\n\tnew_pid->pid = pid;\n\tnew_pid->next = *pids;\n\t*pids = new_pid;\n}\n\nstatic bool match_pid(struct pids *pids, int pid)\n{\n\twhile (pids) {\n\t\tif (pids->pid == pid)\n\t\t\treturn true;\n\t\tpids = pids->next;\n\t}\n\treturn false;\n}\n\nstatic int callback(struct tep_event *event, struct tep_record *record, int cpu,\n\t\t    void *data)\n{\n\tstruct trace_fields *fields = data;\n\tstruct tep_handle *tep = event->tep;\n\tunsigned long long val;\n\tint flags;\n\tint type;\n\tint pid;\n\tint ret;\n\n\tret = tep_read_number_field(fields->common_pid, record->data, &val);\n\tif (ret < 0)\n\t\treturn 0;\n\n\tflags = tep_data_flags(tep, record);\n\n\t/* Ignore events in interrupts */\n\tif (flags & (TRACE_FLAG_HARDIRQ | TRACE_FLAG_SOFTIRQ))\n\t\treturn 0;\n\n\t/*\n\t * First make sure that this event comes from a PID from\n\t * this task (or a task woken by this task)\n\t */\n\tpid = val;\n\tif (!match_pid(fields->pids, pid))\n\t\treturn 0;\n\n\ttype = tep_data_type(tep, record);\n\n\t/*\n\t * If this event is a kvm_exit, we have our PID\n\t * and we can stop processing.\n\t */\n\tif (type == fields->kvm_exit->id) {\n\t\tfields->found_pid = pid;\n\t\treturn -1;\n\t}\n\n\tif (type != fields->sched_waking->id)\n\t\treturn 0;\n\n\tret = tep_read_number_field(fields->sched_next, record->data, &val);\n\tif (ret < 0)\n\t\treturn 0;\n\n\t/* This is a task woken by our task or a chain of wake ups */\n\tadd_pid(&fields->pids, (int)val);\n\treturn 0;\n}\n\nstatic int find_tgid(int pid)\n{\n\tFILE *fp;\n\tchar *path;\n\tchar *buf = NULL;\n\tchar *save;\n\tsize_t l = 0;\n\tint tgid = -1;\n\n\tif (asprintf(&path, \"/proc/%d/status\", pid) < 0)\n\t\treturn -1;\n\n\tfp = fopen(path, \"r\");\n\tfree(path);\n\tif (!fp)\n\t\treturn -1;\n\n\twhile (getline(&buf, &l, fp) > 0) {\n\t\tchar *tok;\n\n\t\tif (strncmp(buf, \"Tgid:\", 5) != 0)\n\t\t\tcontinue;\n\t\ttok = strtok_r(buf, \":\", &save);\n\t\tif (!tok)\n\t\t\tcontinue;\n\t\ttok = strtok_r(NULL, \":\", &save);\n\t\tif (!tok)\n\t\t\tcontinue;\n\t\twhile (isspace(*tok))\n\t\t\ttok++;\n\t\ttgid = strtol(tok, NULL, 0);\n\t\tbreak;\n\t}\n\tfree(buf);\n\tfclose(fp);\n\n\treturn tgid;\n}\n\nstatic int stop_trace_connect(struct tracefs_instance *open_instance)\n{\n\tconst char *systems[] = { \"kvm\", \"sched\", NULL};\n\tstruct tep_handle *tep;\n\tstruct trace_fields trace_fields;\n\tint tgid = -1;\n\n\tif (!open_instance)\n\t\treturn -1;\n\n\t/* The connection is finished, stop tracing, we have what we want */\n\ttracefs_trace_off(open_instance);\n\ttracefs_event_disable(open_instance, NULL, NULL);\n\n\ttep = tracefs_local_events_system(NULL, systems);\n\n\ttrace_fields.found_pid = -1;\n\ttrace_fields.sched_waking = tep_find_event_by_name(tep, \"sched\", \"sched_waking\");\n\tif (!trace_fields.sched_waking)\n\t\tgoto out;\n\ttrace_fields.kvm_exit = tep_find_event_by_name(tep, \"kvm\", \"kvm_exit\");\n\tif (!trace_fields.kvm_exit)\n\t\tgoto out;\n\ttrace_fields.common_pid = tep_find_common_field(trace_fields.sched_waking,\n\t\t\t\t\t\t\t\"common_pid\");\n\tif (!trace_fields.common_pid)\n\t\tgoto out;\n\ttrace_fields.sched_next = tep_find_any_field(trace_fields.sched_waking,\n\t\t\t\t\t\t\t\"pid\");\n\tif (!trace_fields.sched_next)\n\t\tgoto out;\n\n\ttrace_fields.pids = NULL;\n\tadd_pid(&trace_fields.pids, getpid());\n\ttracefs_iterate_raw_events(tep, open_instance, NULL, 0, callback, &trace_fields);\n\tfree_pids(trace_fields.pids);\n out:\n\ttracefs_instance_destroy(open_instance);\n\ttracefs_instance_free(open_instance);\n\n\tif (trace_fields.found_pid > 0)\n\t\ttgid = find_tgid(trace_fields.found_pid);\n\n\treturn tgid;\n}\n\n/*\n * In order to find the guest that is associated to the given cid,\n * trace the sched_waking and kvm_exit events, connect to the cid\n * (doesn't matter what port, use -1 to not connect to anything)\n * and find what task gets woken up from this code and calls kvm_exit,\n * then that is the task that is running the guest.\n * Then look at the /proc/<guest-pid>/status file to find the task group\n * id (Tgid), and this is the PID of the task running all the threads.\n */\nstatic void find_pid_by_cid(struct trace_guest *guest)\n{\n\tstruct tracefs_instance *instance;\n\tint fd;\n\n\tinstance = start_trace_connect();\n\tfd = tcmd_vsock_open(guest->cid, -1);\n\tguest->pid = stop_trace_connect(instance);\n\t/* Just in case! */\n\tif (fd >= 0)\n\t\tclose(fd);\n}\n\nstruct trace_guest *trace_get_guest(unsigned int cid, const char *name)\n{\n\tstruct trace_guest *guest = NULL;\n\n\tif (name) {\n\t\tguest = get_guest_by_name(name);\n\t\tif (guest)\n\t\t\treturn guest;\n\t}\n\n\tif (cid > 0) {\n\t\tguest = get_guest_by_cid(cid);\n\t\tif (!guest && name)\n\t\t\tguest = add_guest(cid, name);\n\t}\n\treturn guest;\n}\n\n#define VM_CID_CMD\t\"virsh dumpxml\"\n#define VM_CID_LINE\t\"<cid auto=\"\n#define VM_CID_ID\t\"address='\"\nstatic void read_guest_cid(char *name)\n{\n\tchar *cmd = NULL;\n\tchar line[512];\n\tchar *cid;\n\tunsigned int cid_id = 0;\n\tFILE *f;\n\n\tasprintf(&cmd, \"%s %s\", VM_CID_CMD, name);\n\tf = popen(cmd, \"r\");\n\tfree(cmd);\n\tif (f == NULL)\n\t\treturn;\n\n\twhile (fgets(line, sizeof(line), f) != NULL) {\n\t\tif (!strstr(line, VM_CID_LINE))\n\t\t\tcontinue;\n\t\tcid = strstr(line, VM_CID_ID);\n\t\tif (!cid)\n\t\t\tcontinue;\n\t\tcid_id = strtol(cid + strlen(VM_CID_ID), NULL, 10);\n\t\tif ((cid_id == INT_MIN || cid_id == INT_MAX) && errno == ERANGE)\n\t\t\tcontinue;\n\t\tadd_guest(cid_id, name);\n\t\tbreak;\n\t}\n\n\t/* close */\n\tpclose(f);\n}\n\n#define VM_NAME_CMD\t\"virsh list --name\"\nvoid read_qemu_guests(void)\n{\n\tchar name[256];\n\tFILE *f;\n\n\tf = popen(VM_NAME_CMD, \"r\");\n\tif (f == NULL)\n\t\treturn;\n\n\twhile (fgets(name, sizeof(name), f) != NULL) {\n\t\tif (name[0] == '\\n')\n\t\t\tcontinue;\n\t\tif (name[strlen(name) - 1] == '\\n')\n\t\t\tname[strlen(name) - 1] = '\\0';\n\t\tread_guest_cid(name);\n\t}\n\n\t/* close */\n\tpclose(f);\n}\n\nint get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu)\n{\n\tint i;\n\n\tif (!guests)\n\t\treturn -1;\n\n\tfor (i = 0; i < guests_len; i++) {\n\t\tif (guests[i].cpu_pid < 0 || guest_vcpu >= guests[i].cpu_max)\n\t\t\tcontinue;\n\t\tif (guest_cid == guests[i].cid)\n\t\t\treturn guests[i].cpu_pid[guest_vcpu];\n\t}\n\treturn -1;\n}\n\n/**\n * trace_add_guest_info - Add the guest info into the trace file option\n * @handle: The file handle that the guest info option is added to\n * @instance: The instance that that represents the guest\n *\n * Adds information about the guest from the @instance into an option\n * for the @instance. It records the trace_id, the number of CPUs,\n * as well as the PIDs of the host that represent the CPUs.\n */\nvoid\ntrace_add_guest_info(struct tracecmd_output *handle, struct buffer_instance *instance)\n{\n\tunsigned long long trace_id;\n\tstruct trace_guest *guest;\n\tconst char *name;\n\tchar *buf, *p;\n\tint cpus;\n\tint size;\n\tint pid;\n\tint i;\n\n\tif (is_network(instance)) {\n\t\tname = instance->name;\n\t\tcpus = instance->cpu_count;\n\t\ttrace_id = instance->trace_id;\n\t} else {\n\t\tguest = trace_get_guest(instance->cid, NULL);\n\t\tif (!guest)\n\t\t\treturn;\n\t\tcpus = guest->cpu_max;\n\t\tname = guest->name;\n\t\t/*\n\t\t * If this is a proxy, the trace_id of the guest is\n\t\t * in the guest descriptor (added in trace_tsync_as_host().\n\t\t */\n\t\tif (guest->trace_id)\n\t\t\ttrace_id = guest->trace_id;\n\t\telse\n\t\t\ttrace_id = instance->trace_id;\n\t}\n\n\tsize = strlen(name) + 1;\n\tsize += sizeof(long long);\t/* trace_id */\n\tsize += sizeof(int);\t\t/* cpu count */\n\tsize += cpus * 2 * sizeof(int);\t/* cpu,pid pair */\n\n\tbuf = calloc(1, size);\n\tif (!buf)\n\t\treturn;\n\tp = buf;\n\tstrcpy(p, name);\n\tp += strlen(name) + 1;\n\n\tmemcpy(p, &trace_id, sizeof(long long));\n\tp += sizeof(long long);\n\n\tmemcpy(p, &cpus, sizeof(int));\n\tp += sizeof(int);\n\tfor (i = 0; i < cpus; i++) {\n\t\tif (is_network(instance))\n\t\t\tpid = -1;\n\t\telse\n\t\t\tpid = guest->cpu_pid[i];\n\t\tmemcpy(p, &i, sizeof(int));\n\t\tp += sizeof(int);\n\t\tmemcpy(p, &pid, sizeof(int));\n\t\tp += sizeof(int);\n\t}\n\n\ttracecmd_add_option(handle, TRACECMD_OPTION_GUEST, size, buf);\n\tfree(buf);\n}\n"
  },
  {
    "path": "tracecmd/trace-vsock.c",
    "content": "#include <unistd.h>\n#include <errno.h>\n#include <arpa/inet.h>\n#include <sys/ioctl.h>\n#include <linux/vm_sockets.h>\n\n#include \"trace-cmd-private.h\"\n\nint __hidden tcmd_vsock_open(unsigned int cid, unsigned int port)\n{\n\tstruct sockaddr_vm addr = {\n\t\t.svm_family = AF_VSOCK,\n\t\t.svm_cid = cid,\n\t\t.svm_port = port,\n\t};\n\tint sd;\n\n\tsd = socket(AF_VSOCK, SOCK_STREAM, 0);\n\tif (sd < 0)\n\t\treturn -errno;\n\n\tif (connect(sd, (struct sockaddr *)&addr, sizeof(addr))) {\n\t\tclose(sd);\n\t\treturn -errno;\n\t}\n\n\treturn sd;\n}\n\nint __hidden tcmd_vsock_make(unsigned int port)\n{\n\tstruct sockaddr_vm addr = {\n\t\t.svm_family = AF_VSOCK,\n\t\t.svm_cid = VMADDR_CID_ANY,\n\t\t.svm_port = port,\n\t};\n\tint sd;\n\n\tsd = socket(AF_VSOCK, SOCK_STREAM, 0);\n\tif (sd < 0)\n\t\treturn -errno;\n\n\tsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));\n\n\tif (bind(sd, (struct sockaddr *)&addr, sizeof(addr)))\n\t\tgoto error;\n\n\tif (listen(sd, SOMAXCONN))\n\t\tgoto error;\n\n\treturn sd;\n\nerror:\n\tclose(sd);\n\treturn -errno;\n}\n\nint __hidden tcmd_vsock_make_any(void)\n{\n\treturn tcmd_vsock_make(VMADDR_PORT_ANY);\n}\n\nint __hidden tcmd_vsock_get_port(int sd, unsigned int *port)\n{\n\tstruct sockaddr_vm addr;\n\tsocklen_t addr_len = sizeof(addr);\n\n\tif (getsockname(sd, (struct sockaddr *)&addr, &addr_len))\n\t\treturn -errno;\n\n\tif (addr.svm_family != AF_VSOCK)\n\t\treturn -EINVAL;\n\n\tif (port)\n\t\t*port = addr.svm_port;\n\n\treturn 0;\n}\n\nint get_vsocket_params(int fd, unsigned int *lcid, unsigned int *rcid)\n{\n\tstruct sockaddr_vm addr;\n\tsocklen_t addr_len = sizeof(addr);\n\n\tif (lcid) {\n\t\tmemset(&addr, 0, sizeof(addr));\n\t\tif (getsockname(fd, (struct sockaddr *)&addr, &addr_len))\n\t\t\treturn -1;\n\t\tif (addr.svm_family != AF_VSOCK)\n\t\t\treturn -1;\n\t\t*lcid = addr.svm_cid;\n\t}\n\n\tif (rcid) {\n\t\tmemset(&addr, 0, sizeof(addr));\n\t\taddr_len = sizeof(addr);\n\t\tif (getpeername(fd, (struct sockaddr *)&addr, &addr_len))\n\t\t\treturn -1;\n\t\tif (addr.svm_family != AF_VSOCK)\n\t\t\treturn -1;\n\t\t*rcid = addr.svm_cid;\n\t}\n\n\treturn 0;\n}\n\nint trace_vsock_print_connection(int fd)\n{\n\tstruct sockaddr_vm vm_addr;\n\tsocklen_t addr_len;\n\tint cid, port;\n\n\taddr_len = sizeof(vm_addr);\n\tif (getpeername(fd, (struct sockaddr *)&vm_addr, &addr_len))\n\t\treturn -1;\n\tif (vm_addr.svm_family != AF_VSOCK)\n\t\treturn -1;\n\tcid = vm_addr.svm_cid;\n\tport = vm_addr.svm_port;\n\tif (tracecmd_get_debug())\n\t\ttracecmd_debug(\"Connected to @%u:%u fd:%d\\n\", cid, port, fd);\n\telse\n\t\ttracecmd_plog(\"Connected to @%u:%u\\n\", cid, port);\n\treturn 0;\n}\n\nstatic int try_splice_read_vsock(void)\n{\n\tint ret, sd, brass[2];\n\n\tsd = socket(AF_VSOCK, SOCK_STREAM, 0);\n\tif (sd < 0)\n\t\treturn -errno;\n\n\tret = pipe(brass);\n\tif (ret < 0)\n\t\tgoto out_close_sd;\n\n\t/*\n\t * On kernels that don't support splice reading from vsockets\n\t * this will fail with EINVAL, or ENOTCONN otherwise.\n\t * Technically, it should never succeed but if it does, claim splice\n\t * reading is supported.\n\t */\n\tret = splice(sd, NULL, brass[1], NULL, 10, 0);\n\tif (ret < 0)\n\t\tret = errno != EINVAL;\n\telse\n\t\tret = 1;\n\n\tclose(brass[0]);\n\tclose(brass[1]);\nout_close_sd:\n\tclose(sd);\n\treturn ret;\n}\n\nbool __hidden tcmd_vsock_can_splice_read(void)\n{\n\tstatic bool initialized, res;\n\n\tif (initialized)\n\t\treturn res;\n\n\tres = try_splice_read_vsock() > 0;\n\tinitialized = true;\n\treturn res;\n}\n\n#define GET_LOCAL_CID\t0x7b9\n\nint __hidden tcmd_vsock_local_cid(void)\n{\n\tint cid;\n\tint fd;\n\n\tfd = open(\"/dev/vsock\", O_RDONLY);\n\tif (fd < 0)\n\t\treturn -errno;\n\n\tif (ioctl(fd, GET_LOCAL_CID, &cid))\n\t\tcid = -errno;\n\n\tclose(fd);\n\treturn cid;\n}\n"
  },
  {
    "path": "utest/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n\ninclude $(src)/scripts/utils.mk\n\nbdir:=$(obj)/utest\n\nTARGETS = $(bdir)/trace-utest\n\nOBJS =\nOBJS += trace-utest.o\nOBJS += tracecmd-utest.o\n\nLIBS += $(LIBTRACECMD_STATIC) -lcunit $(LIBTRACEEVENT_LDLAGS) $(LIBTRACEFS_LDLAGS)\n\nLIBS += $(ZLIB_LDLAGS) $(LIBZSTD_LDLAGS)\n\nOBJS := $(OBJS:%.o=$(bdir)/%.o)\nDEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)\n\nVALGRIND = $(shell which valgrind)\n\n$(bdir):\n\t@mkdir -p $(bdir)\n\n$(OBJS): | $(bdir)\n$(DEPS): | $(bdir)\n\n$(bdir)/trace-utest: $(OBJS) $(LIBTRACECMD_STATIC)\n\t$(Q)$(do_app_build)\n\n$(bdir)/%.o: %.c\n\t$(Q)$(call do_fpic_compile)\n\n$(DEPS): $(bdir)/.%.d: %.c\n\t$(Q)$(CC) -M $(CPPFLAGS) $(CFLAGS) $< > $@\n\t$(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@\n\n$(OBJS): $(bdir)/%.o : $(bdir)/.%.d\n\ndep_includes := $(wildcard $(DEPS))\n\ntest: $(TARGETS)\n\ntest_mem: test\nifeq (, $(VALGRIND))\n\t$(error \"No valgrind in $(PATH), cannot run memory test\")\nendif\nifneq ($(shell id -u), 0)\n\t$(error \"The memory test should be run as root, as it reuqires full access to tracefs\")\nendif\n\tCK_FORK=no $(VALGRIND) \\\n\t\t--show-leak-kinds=all --leak-resolution=high \\\n\t\t--leak-check=full --show-possibly-lost=yes \\\n\t\t--track-origins=yes -s \\\n\t\t$(bdir)/trace-utest\n\nclean:\n\t$(RM) $(TARGETS) $(bdir)/*.o $(bdir)/.*.d\n"
  },
  {
    "path": "utest/README",
    "content": "\nUnit tests for trace-cmd libraries. The tests use CUnit framework:\n http://cunit.sourceforge.net/\nwhich must be pre installed on the system, before building the unit tests.\nThe framework can be downloaded, compiled and installed manually, or\nusing a precompiled distro package:\n\n Fedora:\n\t CUnit\n\t CUnit-devel\n\n Ubuntu and Debian:\n\tlibcunit1\n\tlibcunit1-doc\n\tlibcunit1-dev\n"
  },
  {
    "path": "utest/meson.build",
    "content": "# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (c) 2023 Daniel Wagner, SUSE LLC\n\nsources = [\n    'tracecmd-utest.c',\n    'trace-utest.c',\n]\n\ne = executable(\n   'trace-utest',\n   sources,\n   include_directories: [\n       incdir,\n       libtracecmd_incdir,\n       libtracecmd_private_incdir,\n       libtracecmd_ext_incdir],\n   dependencies: [\n       libtraceevent_dep,\n       libtracefs_dep,\n       zlib_dep,\n       libzstd_dep,\n       cunit_dep],\n   link_with: [static_libtracecmd])\n\ntest('trace-utest', e)\n"
  },
  {
    "path": "utest/trace-utest.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n *\n */\n#include <stdio.h>\n#include <unistd.h>\n#include <getopt.h>\n#include <stdlib.h>\n#include <libgen.h>\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"trace-utest.h\"\n\nconst char *argv0;\nbool show_output;\n\nenum unit_tests {\n\tRUN_NONE\t= 0,\n\tRUN_TRACECMD\t= (1 << 0),\n\tRUN_ALL\t\t= 0xFFFF\n};\n\nstatic void print_help(char **argv)\n{\n\tprintf(\"Usage: %s [OPTIONS]\\n\", basename(argv[0]));\n\tprintf(\"\\t-s, --silent\\tPrint test summary\\n\");\n\tprintf(\"\\t-r, --run test\\tRun specific test:\\n\");\n\tprintf(\"\\t\\t  trace-cmd   run trace-cmd tests\\n\");\n\tprintf(\"\\t-h, --help\\tPrint usage information\\n\");\n\texit(0);\n}\n\nint main(int argc, char **argv)\n{\n\tCU_BasicRunMode verbose = CU_BRM_VERBOSE;\n\tenum unit_tests tests = RUN_NONE;\n\n\targv0 = argv[0];\n\n\tfor (;;) {\n\t\tint c;\n\t\tint index = 0;\n\t\tconst char *opts = \"+hsr:v\";\n\t\tstatic struct option long_options[] = {\n\t\t\t{\"silent\", no_argument, NULL, 's'},\n\t\t\t{\"run\", required_argument, NULL, 'r'},\n\t\t\t{\"verbose\", no_argument, NULL, 'v'},\n\t\t\t{\"help\", no_argument, NULL, 'h'},\n\t\t\t{NULL, 0, NULL, 0}\n\t\t};\n\n\t\tc = getopt_long (argc, argv, opts, long_options, &index);\n\t\tif (c == -1)\n\t\t\tbreak;\n\t\tswitch (c) {\n\t\tcase 'r':\n\t\t\tif (strcmp(optarg, \"trace-cmd\") == 0)\n\t\t\t\ttests |= RUN_TRACECMD;\n\t\t\telse\n\t\t\t\tprint_help(argv);\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tverbose = CU_BRM_SILENT;\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\tshow_output = true;\n\t\t\tbreak;\n\t\tcase 'h':\n\t\tdefault:\n\t\t\tprint_help(argv);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (tests == RUN_NONE)\n\t\ttests = RUN_ALL;\n\n\tif (CU_initialize_registry() != CUE_SUCCESS) {\n\t\tprintf(\"Test registry cannot be initialized\\n\");\n\t\treturn -1;\n\t}\n\n\tif (tests & RUN_TRACECMD)\n\t\ttest_tracecmd_lib();\n\n\tCU_basic_set_mode(verbose);\n\tCU_basic_run_tests();\n\tCU_cleanup_registry();\n\treturn 0;\n}\n"
  },
  {
    "path": "utest/trace-utest.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1 */\n/*\n * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n *\n */\n#ifndef _TRACE_UTEST_H_\n#define _TRACE_UTEST_H_\n\n#include <stdbool.h>\n\nextern const char *argv0;\nextern bool show_output;\n\nvoid test_tracecmd_lib(void);\n\n#endif /* _TRACE_UTEST_H_ */\n"
  },
  {
    "path": "utest/tracecmd-utest.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1\n/*\n * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>\n *\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <time.h>\n#include <dirent.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <sys/types.h>\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include <trace-cmd.h>\n\n#include \"trace-utest.h\"\n\n#ifndef PATH_MAX\n#define PATH_MAX 1024\n#endif\n\nstatic char tracecmd_exec[PATH_MAX];\n\n#define TRACECMD_SUITE\t\t\"trace-cmd\"\n#define TRACECMD_FILE\t\t\"__trace_test__.dat\"\n#define TRACECMD_FILE2\t\t\"__trace_test__2.dat\"\n#define TRACECMD_OUT\t\t\"-o\", TRACECMD_FILE\n#define TRACECMD_OUT2\t\t\"-o\", TRACECMD_FILE2\n#define TRACECMD_IN\t\t\"-i\", TRACECMD_FILE\n#define TRACECMD_IN2\t\t\"-i\", TRACECMD_FILE2\n\n#define TRACECMD_SQL_HIST\t\"SELECT irq FROM irq_handler_entry\"\n#define TRACECMD_SQL_READ_HIST\t\"show\", \"--hist\", \"irq_handler_entry\"\n\n#define SYNTH_EVENT\t\t\"wakeup\"\n#define TRACECMD_SQL_SYNTH\t\"-e\", \"-n\", SYNTH_EVENT, \"SELECT start.pid AS this_pid, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS delta FROM sched_waking as start JOIN sched_switch AS end ON start.pid = end.next_pid\"\n#define TRACECMD_SQL_START_SYNTH \"start\", \"-e\", SYNTH_EVENT\n\nstatic char **get_args(const char *cmd, va_list ap)\n{\n\tconst char *param;\n\tchar **argv;\n\tchar **tmp;\n\n\targv = tracefs_list_add(NULL, tracecmd_exec);\n\tif (!argv)\n\t\treturn NULL;\n\n\ttmp = tracefs_list_add(argv, cmd);\n\tif (!tmp)\n\t\tgoto fail;\n\targv = tmp;\n\n\tfor (param = va_arg(ap, const char *);\n\t     param; param = va_arg(ap, const char *)) {\n\t\ttmp = tracefs_list_add(argv, param);\n\t\tif (!tmp)\n\t\t\tgoto fail;\n\t\targv = tmp;\n\t}\n\n\treturn argv;\n fail:\n\ttracefs_list_free(argv);\n\treturn NULL;\n}\n\nstatic void silent_output(void)\n{\n\tclose(STDOUT_FILENO);\n\topen(\"/dev/null\", O_WRONLY);\n\tclose(STDERR_FILENO);\n\topen(\"/dev/null\", O_WRONLY);\n}\n\nstatic int wait_for_exec(int pid)\n{\n\tint status;\n\tint ret;\n\n\tret = waitpid(pid, &status, 0);\n\tif (ret != pid)\n\t\treturn -1;\n\n\treturn WEXITSTATUS(status) ? -1 : 0;\n}\n\nstatic int run_trace(const char *cmd, ...)\n{\n\tchar **argv;\n\tva_list ap;\n\tint ret = -1;\n\tpid_t pid;\n\n\tva_start(ap, cmd);\n\targv = get_args(cmd, ap);\n\tva_end(ap);\n\n\tif (!argv)\n\t\treturn -1;\n\n\tpid = fork();\n\tif (pid < 0)\n\t\tgoto out;\n\n\tif (!pid) {\n\t\tif (!show_output)\n\t\t\tsilent_output();\n\t\tret = execvp(tracecmd_exec, argv);\n\t\texit (ret);\n\t}\n\n\tret = wait_for_exec(pid);\n out:\n\ttracefs_list_free(argv);\n\treturn ret;\n}\n\nstatic int pipe_it(int *ofd, int *efd, int (*func)(void *),\n\t\t   void *data)\n{\n\tint obrass[2];\n\tint ebrass[2];\n\tpid_t pid;\n\tint ret;\n\n\tif (pipe(obrass) < 0)\n\t\treturn -1;\n\n\tif (pipe(ebrass) < 0)\n\t\tgoto fail_out;\n\n\tpid = fork();\n\tif (pid < 0)\n\t\tgoto fail;\n\n\tif (!pid) {\n\t\tchar shret[32];\n\n\t\tclose(obrass[0]);\n\t\tclose(STDOUT_FILENO);\n\t\tif (dup2(obrass[1], STDOUT_FILENO) < 0)\n\t\t\texit(-1);\n\n\t\tclose(ebrass[0]);\n\t\tclose(STDERR_FILENO);\n\t\tif (dup2(obrass[1], STDERR_FILENO) < 0)\n\t\t\texit(-1);\n\n\t\tret = func(data);\n\n\t\t/*\n\t\t * valgrind triggers its reports when the application\n\t\t * exits. If the application does a fork() and the child\n\t\t * exits, it will still trigger the valgrind report for\n\t\t * all the allocations that were not freed by the parent.\n\t\t *\n\t\t * To prevent valgrind from triggering, do an execl() on\n\t\t * a basic shell that will simply exit with the return value.\n\t\t * This will quiet valgrind from reporting memory that has\n\t\t * been allocated by the parent up to here.\n\t\t */\n\t\tsnprintf(shret, 32, \"exit %d\", ret);\n\t\texecl(\"/usr/bin/sh\", \"/usr/bin/sh\", \"-c\", shret, NULL);\n\t\texecl(\"/bin/sh\", \"/bin/sh\", \"-c\", shret, NULL);\n\n\t\t/* If the above execl() fails, simply do an exit */\n\t\texit(ret);\n\t}\n\n\tclose(obrass[1]);\n\tclose(ebrass[1]);\n\n\t*ofd = obrass[0];\n\t*efd = ebrass[0];\n\n\treturn pid;\n\n fail:\n\tclose(ebrass[0]);\n\tclose(ebrass[1]);\n fail_out:\n\tclose(obrass[0]);\n\tclose(obrass[1]);\n\treturn -1;\n}\n\nstruct do_grep {\n\tconst char\t\t*cmd;\n\tva_list\t\t\t*ap;\n};\n\nstatic int do_grep(void *data)\n{\n\tstruct do_grep *gdata = data;\n\tchar **argv;\n\tint ret;\n\n\targv = get_args(gdata->cmd, *gdata->ap);\n\tif (!argv)\n\t\texit(-1);\n\n\tret = execvp(tracecmd_exec, argv);\n\ttracefs_list_free(argv);\n\treturn ret;\n}\n\nstruct do_grep_it {\n\tconst char\t\t*match;\n\tconst char\t\t*cmd;\n\tva_list\t\t\t*ap;\n};\n\nstatic int do_grep_it(void *data)\n{\n\tstruct do_grep_it *dgdata = data;\n\tstruct do_grep gdata;\n\tFILE *fp;\n\tregex_t reg;\n\tchar *buf = NULL;\n\tssize_t n;\n\tsize_t l = 0;\n\tint ofd;\n\tint efd;\n\tint pid;\n\tint ret;\n\n\tif (regcomp(&reg, dgdata->match, REG_ICASE|REG_NOSUB))\n\t\treturn -1;\n\n\tgdata.cmd = dgdata->cmd;\n\tgdata.ap = dgdata->ap;\n\tpid = pipe_it(&ofd, &efd, do_grep, &gdata);\n\n\tif (pid < 0) {\n\t\tregfree(&reg);\n\t\treturn -1;\n\t}\n\n\tfp = fdopen(ofd, \"r\");\n\tif (!fp)\n\t\tgoto out;\n\n\tdo {\n\t\tn = getline(&buf, &l, fp);\n\t\tif (n > 0 && regexec(&reg, buf, 0, NULL, 0) == 0)\n\t\t\tprintf(\"%s\", buf);\n\t} while (n >= 0);\n\n\tfree(buf);\n out:\n\tret = wait_for_exec(pid);\n\tif (fp)\n\t\tfclose(fp);\n\telse\n\t\tperror(\"fp\");\n\tclose(ofd);\n\tclose(efd);\n\tregfree(&reg);\n\n\treturn ret > 0 ? 0 : ret;\n}\n\nstruct do_grep_match {\n\tconst char\t\t*match;\n\tconst char\t\t*cmd;\n\tva_list\t\t\t*ap;\n};\n\nstatic int grep_match(const char *match, const char *cmd, ...)\n{\n\tstruct do_grep_it gdata;\n\tFILE *fp;\n\tva_list ap;\n\tchar *buf = NULL;\n\tssize_t n;\n\tsize_t l = 0;\n\tbool found = false;\n\tint ofd;\n\tint efd;\n\tint pid;\n\tint ret;\n\n\tva_start(ap, cmd);\n\tgdata.match = match;\n\tgdata.cmd = cmd;\n\tgdata.ap = &ap;\n\tpid = pipe_it(&ofd, &efd, do_grep_it, &gdata);\n\tva_end(ap);\n\n\tif (pid < 0)\n\t\treturn -1;\n\n\tfp = fdopen(ofd, \"r\");\n\tif (!fp)\n\t\tgoto out;\n\n\tdo {\n\t\tn = getline(&buf, &l, fp);\n\t\tif (n > 0) {\n\t\t\tif (show_output)\n\t\t\t\tprintf(\"%s\", buf);\n\t\t\tfound = true;\n\t\t}\n\t} while (n >= 0);\n\n\tfree(buf);\n out:\n\tret = wait_for_exec(pid);\n\tif (ret)\n\t\tn = 1;\n\tif (fp)\n\t\tfclose(fp);\n\telse {\n\t\tperror(\"fp\");\n\t\tclose(ofd);\n\t}\n\tclose(efd);\n\n\treturn found ? 0 : 1;\n}\n\nstatic void test_trace_record_report(void)\n{\n\tint ret;\n\n\tret = run_trace(\"record\", TRACECMD_OUT, \"-e\", \"sched\", \"sleep\", \"1\", NULL);\n\tCU_TEST(ret == 0);\n\tret = run_trace(\"convert\", \"--file-version\", \"6\", TRACECMD_IN, TRACECMD_OUT2, NULL);\n\tCU_TEST(ret == 0);\n}\n\nstatic void test_trace_sqlhist_hist(void)\n{\n\tint ret;\n\n\tret = run_trace(\"sqlhist\", \"-e\", TRACECMD_SQL_HIST, NULL);\n\tCU_TEST(ret == 0);\n\tret = grep_match(\" *Hits: [0-9][0-9]*\", TRACECMD_SQL_READ_HIST, NULL);\n\tCU_TEST(ret == 0);\n\tret = run_trace(\"sqlhist\", TRACECMD_SQL_SYNTH, NULL);\n\tCU_TEST(ret == 0);\n\tret = run_trace(TRACECMD_SQL_START_SYNTH, NULL);\n\tCU_TEST(ret == 0);\n\tsleep(1);\n\tret = grep_match(SYNTH_EVENT \":\", \"show\", NULL);\n\tCU_TEST(ret == 0);\n\t/* Ensure synthetic events remain untouched after \"trace-cmd reset -k synth\". */\n\tret = run_trace(\"reset\", \"-k\", \"synth\", NULL);\n\tCU_TEST(ret == 0);\n\tret = grep_match(SYNTH_EVENT, \"stat\", NULL);\n\tCU_TEST(ret == 0);\n\n\ttracefs_instance_reset(NULL);\n}\n\nstatic int read_stats(const char *out, const char *match, const char *cmd, ...)\n{\n\tstruct do_grep_it gdata;\n\tFILE *fp;\n\tva_list ap;\n\tbool found = false;\n\tchar *buf = NULL;\n\tchar *p;\n\tssize_t n;\n\tsize_t l = 0;\n\tint ofd;\n\tint efd;\n\tint pid;\n\tint ret;\n\tint val;\n\n\tva_start(ap, cmd);\n\tgdata.match = match;\n\tgdata.cmd = cmd;\n\tgdata.ap = &ap;\n\tpid = pipe_it(&ofd, &efd, do_grep_it, &gdata);\n\tva_end(ap);\n\n\tif (pid < 0)\n\t\treturn -1;\n\n\tfp = fdopen(ofd, \"r\");\n\tif (!fp)\n\t\tgoto out;\n\n\tdo {\n\t\tn = getline(&buf, &l, fp);\n\t\tif (n > 0) {\n\t\t\tfor (p = buf; isspace(*p); p++)\n\t\t\t\t;\n\t\t\tval = atoi(p);\n\t\t\tfound = true;\n\t\t\tif (show_output)\n\t\t\t\tprintf(\"%s\", buf);\n\t\t\tCU_TEST(val < 10000000);\n\t\t}\n\t} while (n >= 0);\n\n\tfree(buf);\n out:\n\tret = wait_for_exec(pid);\n\tif (fp)\n\t\tfclose(fp);\n\telse {\n\t\tperror(\"fp\");\n\t}\n\tif (!found)\n\t\tret = -1;\n\tclose(ofd);\n\tclose(efd);\n\treturn ret > 0 ? 0 : ret;\n}\n\nstatic void test_trace_record_max(void)\n{\n\tint ret;\n\n\tret = run_trace(\"record\", TRACECMD_OUT, \"-p\", \"function\", \"-m\", \"5000\",\n\t\t\t\"sleep\", \"10\", NULL);\n\tCU_TEST(ret == 0);\n\n\tret = read_stats(TRACECMD_FILE, \".*bytes in size.*\", \"report\", TRACECMD_IN, \"--stat\", NULL);\n\tCU_TEST(ret == 0);\n}\n\nstatic void test_trace_convert6(void)\n{\n\tstruct stat st;\n\tint ret;\n\n\t/* If the trace data is already created, just use it, otherwise make it again */\n\tif (stat(TRACECMD_FILE, &st) < 0) {\n\t\tret = run_trace(\"record\", TRACECMD_OUT, \"-e\", \"sched\", \"sleep\", \"1\", NULL);\n\t\tCU_TEST(ret == 0);\n\t}\n\tret = grep_match(\"[ \\t]6[ \\t]*\\\\[Version\\\\]\", \"dump\", TRACECMD_IN2, NULL);\n\tCU_TEST(ret == 0);\n}\n\nstruct callback_data {\n\tlong\t\t\tcounter;\n\tstruct trace_seq\tseq;\n};\n\nstatic int read_events(struct tracecmd_input *handle, struct tep_record *record,\n\t\t       int cpu, void *data)\n{\n\tstruct tep_handle *tep = tracecmd_get_tep(handle);\n\tstruct callback_data *cd = data;\n\tstruct trace_seq *seq = &cd->seq;\n\n\tcd->counter++;\n\n\ttrace_seq_reset(seq);\n\ttep_print_event(tep, seq, record, \"%6.1000d\", TEP_PRINT_TIME);\n\ttrace_seq_printf(seq, \" [%03d] \", cpu);\n\ttep_print_event(tep, seq, record, \"%s-%d %s %s\\n\",\n\t\t\tTEP_PRINT_COMM, TEP_PRINT_PID,\n\t\t\tTEP_PRINT_NAME, TEP_PRINT_INFO);\n\tif (show_output)\n\t\ttrace_seq_do_printf(seq);\n\treturn 0;\n}\n\nstatic int read_events_10(struct tracecmd_input *handle, struct tep_record *record,\n\t\t\t  int cpu, void *data)\n{\n\tstruct callback_data *cd = data;\n\n\tread_events(handle, record, cpu, data);\n\treturn  cd->counter < 10 ? 0 : 1;\n}\n\nstatic void test_trace_library_read(void)\n{\n\tstruct tracecmd_input *handle;\n\tstruct callback_data data;\n\tstruct stat st;\n\tint ret;\n\n\tdata.counter = 0;\n\ttrace_seq_init(&data.seq);\n\n\t/* If the trace data is already created, just use it, otherwise make it again */\n\tif (stat(TRACECMD_FILE, &st) < 0) {\n\t\tret = run_trace(\"record\", TRACECMD_OUT, \"-e\", \"sched\", \"sleep\", \"1\", NULL);\n\t\tCU_TEST(ret == 0);\n\t}\n\n\thandle = tracecmd_open(TRACECMD_FILE, 0);\n\tCU_TEST(handle != NULL);\n\tret = tracecmd_iterate_events(handle, NULL, 0, read_events, &data);\n\tCU_TEST(ret == 0);\n\n\ttracecmd_close(handle);\n\n\tCU_TEST(data.counter > 0);\n\ttrace_seq_destroy(&data.seq);\n}\n\nstatic void test_trace_library_read_inc(void)\n{\n\tstruct tracecmd_input *handle;\n\tstruct callback_data data;\n\tstruct stat st;\n\tlong save_count;\n\tlong total = 0;\n\tint ret;\n\n\tdata.counter = 0;\n\ttrace_seq_init(&data.seq);\n\n\t/* If the trace data is already created, just use it, otherwise make it again */\n\tif (stat(TRACECMD_FILE, &st) < 0) {\n\t\tret = run_trace(\"record\", TRACECMD_OUT, \"-e\", \"sched\", \"sleep\", \"1\", NULL);\n\t\tCU_TEST(ret == 0);\n\t}\n\n\t/* First read all again */\n\thandle = tracecmd_open(TRACECMD_FILE, 0);\n\tCU_TEST(handle != NULL);\n\tret = tracecmd_iterate_events(handle, NULL, 0, read_events, &data);\n\tCU_TEST(ret == 0);\n\tCU_TEST(data.counter > 0);\n\n\t/* Save the counter */\n\tsave_count = data.counter;\n\n\ttracecmd_iterate_reset(handle);\n\n\t/* Read 10 at a time */\n\tdo {\n\t\tdata.counter = 0;\n\t\tret = tracecmd_iterate_events(handle, NULL, 0, read_events_10, &data);\n\t\tCU_TEST(ret >= 0);\n\t\tCU_TEST(data.counter <= 10);\n\t\ttotal += data.counter;\n\t} while (data.counter);\n\tCU_TEST(ret == 0);\n\n\tCU_TEST(total == save_count);\n\n\ttrace_seq_destroy(&data.seq);\n\ttracecmd_close(handle);\n}\n\nstatic void test_trace_library_read_back(void)\n{\n\tstruct tracecmd_input *handle;\n\tstruct callback_data data;\n\tstruct stat st;\n\tlong save_count;\n\tint ret;\n\n\tdata.counter = 0;\n\ttrace_seq_init(&data.seq);\n\n\t/* If the trace data is already created, just use it, otherwise make it again */\n\tif (stat(TRACECMD_FILE, &st) < 0) {\n\t\tret = run_trace(\"record\", TRACECMD_OUT, \"-e\", \"sched\", \"sleep\", \"1\", NULL);\n\t\tCU_TEST(ret == 0);\n\t}\n\n\t/* First read all again */\n\thandle = tracecmd_open(TRACECMD_FILE, 0);\n\tCU_TEST(handle != NULL);\n\tret = tracecmd_iterate_events(handle, NULL, 0, read_events, &data);\n\tCU_TEST(ret == 0);\n\tCU_TEST(data.counter > 0);\n\n\t/* Save the counter */\n\tsave_count = data.counter;\n\n\ttracecmd_iterate_reset(handle);\n\n\t/* Read backwards */\n\tdata.counter = 0;\n\tret = tracecmd_iterate_events_reverse(handle, NULL, 0, read_events, &data, false);\n\tCU_TEST(ret == 0);\n\tCU_TEST(data.counter == save_count);\n\n\t/* Read forward again */\n\tdata.counter = 0;\n\tret = tracecmd_iterate_events(handle, NULL, 0, read_events, &data);\n\tCU_TEST(ret == 0);\n\tCU_TEST(data.counter == save_count);\n\n\t/* Read backwards from where we left off */\n\tdata.counter = 0;\n\tret = tracecmd_iterate_events_reverse(handle, NULL, 0, read_events, &data, true);\n\tCU_TEST(ret == 0);\n\tCU_TEST(data.counter == save_count);\n\n\ttrace_seq_destroy(&data.seq);\n\ttracecmd_close(handle);\n}\n\nstatic void test_trace_reset_kprobe(void)\n{\n\tint ret;\n\n\t/* Create a simple kprobe for do_sys_open */\n\tret = tracefs_instance_file_write(NULL, \"kprobe_events\", \"p do_sys_open\");\n\tCU_TEST(ret > 0);\n\n\t/* Ensure the kprobe is listed in \"trace-cmd stat\" output. */\n\tret = grep_match(\"p:kprobes/p_do_sys_open_0 do_sys_open\", \"stat\", NULL);\n\tCU_TEST(ret == 0);\n\n\t/* Issue \"trace-cmd reset\", but keep kprobes. */\n\tret = run_trace(\"reset\", \"-k\", \"kprobe\", NULL);\n\tCU_TEST(ret == 0);\n\n\t/* Verify the kprobe's existence after reset. */\n\tret = grep_match(\"p:kprobes/p_do_sys_open_0 do_sys_open\", \"stat\", NULL);\n\tCU_TEST(ret == 0);\n}\n\nstatic void test_trace_reset_kretprobe(void)\n{\n\tint ret;\n\n\t/* Create a simple kretprobe for do_sys_open */\n\tret = tracefs_instance_file_write(NULL, \"kprobe_events\", \"r do_sys_open\");\n\tCU_TEST(ret > 0);\n\n\t/* Ensure the kretprobe is listed in \"trace-cmd stat\" output. */\n\tret = grep_match(\"r[0-9]*:kprobes/r_do_sys_open_0 do_sys_open\", \"stat\", NULL);\n\tCU_TEST(ret == 0);\n\n\t/* Issue \"trace-cmd reset\", but keep kretprobes. */\n\tret = run_trace(\"reset\", \"-k\", \"kretprobe\", NULL);\n\tCU_TEST(ret == 0);\n\n\t/* Verify the kretprobe's existence after reset. */\n\tret = grep_match(\"r[0-9]*:kprobes/r_do_sys_open_0 do_sys_open\", \"stat\", NULL);\n\tCU_TEST(ret == 0);\n}\n\nstatic void test_trace_reset_uprobe(void)\n{\n\tint ret;\n\n\t/* Create a simple uprobe for do_sys_open */\n\tret = tracefs_instance_file_write(NULL, \"uprobe_events\", \"p /bin/bash:0x4245c0\");\n\tCU_TEST(ret > 0);\n\n\t/* Ensure the uprobe is listed in \"trace-cmd stat\" output. */\n\tret = grep_match(\"p:uprobes/p_bash_0x4245c0 /bin/bash:0x00000000004245c0\", \"stat\", NULL);\n\tCU_TEST(ret == 0);\n\n\t/* Issue \"trace-cmd reset\", but keep uprobes. */\n\tret = run_trace(\"reset\", \"-k\", \"uprobe\", NULL);\n\tCU_TEST(ret == 0);\n\n\t/* Verify the uprobe's existence after reset. */\n\tret = grep_match(\"p:uprobes/p_bash_0x4245c0 /bin/bash:0x00000000004245c0\", \"stat\", NULL);\n\tCU_TEST(ret == 0);\n}\n\nstatic void test_trace_reset_uretprobe(void)\n{\n\tint ret;\n\n\t/* Create a simple uretprobe for do_sys_open */\n\tret = tracefs_instance_file_write(NULL, \"uprobe_events\", \"r /bin/bash:0x4245c0\");\n\tCU_TEST(ret > 0);\n\n\t/* Ensure the uretprobe is listed in \"trace-cmd stat\" output. */\n\tret = grep_match(\"r:uprobes/p_bash_0x4245c0 /bin/bash:0x00000000004245c0\", \"stat\", NULL);\n\tCU_TEST(ret == 0);\n\n\t/* Issue \"trace-cmd reset\", but keep uretprobes. */\n\tret = run_trace(\"reset\", \"-k\", \"uretprobe\", NULL);\n\tCU_TEST(ret == 0);\n\n\t/* Verify the uretprobe's existence after reset. */\n\tret = grep_match(\"r:uprobes/p_bash_0x4245c0 /bin/bash:0x00000000004245c0\", \"stat\", NULL);\n\tCU_TEST(ret == 0);\n}\n\nstatic void test_trace_reset_eprobe(void)\n{\n\tint fd;\n\tbool matched = false;\n\tsize_t l = 0;\n\tssize_t n;\n\tchar *buf = NULL;\n\tstruct tracefs_dynevent *deprobe;\n\tFILE *fp;\n\n\tdeprobe = tracefs_eprobe_alloc(NULL, \"sopen_in\", \"syscalls\", \"sys_enter_openat\", NULL);\n\tCU_TEST(deprobe != NULL);\n\n\tCU_TEST(tracefs_dynevent_create(deprobe) == 0);\n\n\t/* Issue \"trace-cmd reset\", but keep eprobes. */\n\tCU_TEST(run_trace(\"reset\", \"-k\", \"eprobe\", NULL) == 0);\n\n\t/* Verify the eprobe's existence after reset. */\n\tfd = tracefs_instance_file_open(NULL, \"dynamic_events\", O_RDONLY);\n\tCU_TEST(fd != -1);\n\tfp = fdopen(fd, \"r\");\n\tCU_TEST(fp != NULL);\n\n\twhile ((n = getline(&buf, &l, fp)) != -1) {\n\t\tif (!strcmp(buf, \"e:eprobes/sopen_in syscalls.sys_enter_openat\\n\")) {\n\t\t\tmatched = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tfree(buf);\n\n\tfclose(fp);\n\n\tCU_TEST(matched == true);\n\n\tCU_TEST(tracefs_dynevent_destroy(deprobe, false) == 0);\n\n\ttracefs_dynevent_free(deprobe);\n}\n\nstatic void test_trace_reset(void)\n{\n\tchar *str;\n\n\ttest_trace_reset_kprobe();\n\ttest_trace_reset_kretprobe();\n\ttest_trace_reset_uprobe();\n\ttest_trace_reset_uretprobe();\n\ttest_trace_reset_eprobe();\n\n\t/* Destroy all dynamic events. */\n\tCU_TEST(run_trace(\"reset\", NULL) == 0);\n\n\t/* Paranoia check since \"trace-cmd reset\" may tell porkies. */\n\tstr = tracefs_instance_file_read(NULL, \"dynamic_events\", NULL);\n\tCU_TEST(str == NULL);\n\tif (str)\n\t\tfree(str);\n}\n\nstatic int test_suite_destroy(void)\n{\n\tunlink(TRACECMD_FILE);\n\tunlink(TRACECMD_FILE2);\n\treturn 0;\n}\n\nstatic int test_suite_init(void)\n{\n\tstruct stat st;\n\tconst char *p;\n\n\t/* The test must be in the utest directory */\n\tfor (p = argv0 + strlen(argv0) - 1; p > argv0 && *p != '/'; p--)\n\t\t;\n\n\tif (*p == '/')\n\t\tsnprintf(tracecmd_exec, PATH_MAX, \"%.*s/../tracecmd/trace-cmd\",\n\t\t\t (int)(p - argv0), argv0);\n\telse\n\t\tstrncpy(tracecmd_exec, \"../tracecmd/trace-cmd\", PATH_MAX);\n\n\tif (stat(tracecmd_exec, &st) < 0) {\n\t\tfprintf(stderr, \"In tree trace-cmd executable not found\\n\");\n\t\treturn 1;\n\t}\n\n\tif (!(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {\n\t\tfprintf(stderr, \"In tree trace-cmd executable not executable\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\nvoid test_tracecmd_lib(void)\n{\n\tCU_pSuite suite = NULL;\n\n\tsuite = CU_add_suite(TRACECMD_SUITE, test_suite_init, test_suite_destroy);\n\tif (suite == NULL) {\n\t\tfprintf(stderr, \"Suite \\\"%s\\\" cannot be created\\n\", TRACECMD_SUITE);\n\t\treturn;\n\t}\n\tCU_add_test(suite, \"Simple record and report\",\n\t\t    test_trace_record_report);\n\tCU_add_test(suite, \"Create a histogram\",\n\t\t    test_trace_sqlhist_hist);\n\tCU_add_test(suite, \"Test convert from v7 to v6\",\n\t\t    test_trace_convert6);\n\tCU_add_test(suite, \"Use libraries to read file\",\n\t\t    test_trace_library_read);\n\tCU_add_test(suite, \"Use libraries to read file incremental\",\n\t\t    test_trace_library_read_inc);\n\tCU_add_test(suite, \"Use libraries to read file backwards\",\n\t\t    test_trace_library_read_back);\n\tCU_add_test(suite, \"Test max length\",\n\t\t    test_trace_record_max);\n\tCU_add_test(suite, \"Simple reset\", test_trace_reset);\n}\n"
  }
]