VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/linux/LnxPerfHack.cpp

Last change on this file was 99775, checked in by vboxsync, 12 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 KB
Line 
1/* $Id: LnxPerfHack.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * LnxPerfHack - Dirty hack to make perf find our .r0 modules.
4 */
5
6/*
7 * Copyright (C) 2021-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/assert.h>
42#include <iprt/file.h>
43#include <iprt/initterm.h>
44#include <iprt/getopt.h>
45#include <iprt/ldr.h>
46#include <iprt/sort.h>
47#include <iprt/string.h>
48#include <iprt/stream.h>
49#include <iprt/mem.h>
50#include <iprt/message.h>
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56#define LNXPERFILEHDR_MAGIC RT_MAKE_U64_FROM_U8('P','E','R','F','I','L','E','2')
57
58#define LNXPERF_RECORD_MMAP 1
59#define LNXPERF_RECORD_MMAP2 10
60
61#define LNXPERF_RECORD_MISC_CPUMODE_MASK UINT16_C(0x0007)
62#define LNXPERF_RECORD_MISC_CPUMODE_UNKNOWN UINT16_C(0x0000)
63#define LNXPERF_RECORD_MISC_KERNEL UINT16_C(0x0001)
64#define LNXPERF_RECORD_MISC_USER UINT16_C(0x0002)
65#define LNXPERF_RECORD_MISC_HYPERVISOR UINT16_C(0x0003)
66#define LNXPERF_RECORD_MISC_GUEST_KERNEL UINT16_C(0x0004)
67#define LNXPERF_RECORD_MISC_GUEST_USER UINT16_C(0x0005)
68
69
70/*********************************************************************************************************************************
71* Structures and Typedefs *
72*********************************************************************************************************************************/
73/** The file header. */
74typedef struct LNXPERFFILEHDR
75{
76 uint64_t uMagic; /**< LNXPERFILEHDR_MAGIC */
77 uint64_t cbHdr;
78 uint64_t cbAttr;
79 struct LNXPERFFILESECTION
80 {
81 uint64_t off, cb;
82 } Attrs, Data, EventTypes;
83 uint64_t bmAddsFeatures[256/64];
84} LNXPERFFILEHDR;
85typedef LNXPERFFILEHDR *PLNXPERFFILEHDR;
86
87
88typedef struct LNXPERFRECORDHEADER
89{
90 uint32_t uType;
91 uint16_t fMisc;
92 uint16_t cb;
93} LNXPERFRECORDHEADER;
94AssertCompileSize(LNXPERFRECORDHEADER, 8);
95typedef LNXPERFRECORDHEADER *PLNXPERFRECORDHEADER;
96
97typedef struct LNXPERFRECORDMMAP
98{
99 LNXPERFRECORDHEADER Hdr;
100 uint32_t pid;
101 uint32_t tid;
102 uint64_t uAddress;
103 uint64_t cbMapping;
104 uint64_t offFile;
105 RT_FLEXIBLE_ARRAY_EXTENSION
106 char szFilename[RT_FLEXIBLE_ARRAY];
107} LNXPERFRECORDMMAP;
108typedef LNXPERFRECORDMMAP *PLNXPERFRECORDMMAP;
109
110typedef struct MYMODULE
111{
112 uint64_t uAddress;
113 uint64_t cbMapping;
114 uint64_t offFile;
115 const char *pszName;
116 uint32_t cchName;
117 uint16_t cbRecord;
118 uint64_t offRecord;
119} MYMODULE;
120typedef MYMODULE *PMYMODULE;
121
122
123static DECLCALLBACK(int) CompModuleRecordOffset(void const *pvElement1, void const *pvElement2, void *pvUser)
124{
125 PMYMODULE pLeft = (PMYMODULE)pvElement1;
126 PMYMODULE pRight = (PMYMODULE)pvElement2;
127 RT_NOREF(pvUser);
128 return pLeft->offRecord < pRight->offRecord ? -1
129 : pLeft->offRecord > pRight->offRecord ? 1 : 0;
130
131}
132
133
134static DECLCALLBACK(int) CompModuleNameLengthDesc(void const *pvElement1, void const *pvElement2, void *pvUser)
135{
136 PMYMODULE pLeft = (PMYMODULE)pvElement1;
137 PMYMODULE pRight = (PMYMODULE)pvElement2;
138 RT_NOREF(pvUser);
139 return pLeft->cchName < pRight->cchName ? 1
140 : pLeft->cchName > pRight->cchName ? -1 : 0;
141}
142
143
144/** @callback_method_impl{FNRTLDRENUMSEGS} */
145static DECLCALLBACK(int) SegmentEnumCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
146{
147 RT_NOREF(hLdrMod);
148 if (pSeg->pszName && RTStrStartsWith(pSeg->pszName, ".text"))
149 {
150 PMYMODULE pModEntry = (PMYMODULE)pvUser;
151 //pModEntry->offFile = pModEntry->uAddress;
152 pModEntry->uAddress += pSeg->RVA;
153 pModEntry->cbMapping = pSeg->cbMapped;
154 //pModEntry->offFile = pModEntry->uAddress - pSeg->LinkAddress;
155 pModEntry->offFile = RT_MAX(pSeg->offFile, 0);
156 return VINF_CALLBACK_RETURN;
157 }
158 return VINF_SUCCESS;
159}
160
161
162
163int main(int argc, char **argv)
164{
165 int rc = RTR3InitExe(argc, &argv, 0);
166 if (RT_FAILURE(rc))
167 return RTMsgInitFailure(rc);
168
169 static const RTGETOPTDEF s_aOptions[] =
170 {
171 { "--input", 'i', RTGETOPT_REQ_STRING },
172 { "--output", 'o', RTGETOPT_REQ_STRING },
173 { "--module", 'm', RTGETOPT_REQ_STRING },
174 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
175 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
176 };
177 const char *pszInput = NULL;
178 const char *pszOutput = NULL;
179 unsigned cVerbosity = 0;
180 unsigned cModules = 0;
181 MYMODULE aModules[10];
182 unsigned cSkipPatterns = 1;
183 const char *apszSkipPatterns[10] = { "*kallsyms*", };
184
185 int ch;
186 RTGETOPTUNION ValueUnion;
187 RTGETOPTSTATE GetState;
188 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
189 AssertRCReturn(rc, RTMsgErrorExitFailure("RTGetOptInit failed: %Rrc", rc));
190 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
191 {
192 switch (ch)
193 {
194 case 'i':
195 pszInput = ValueUnion.psz;
196 break;
197
198 case 'o':
199 pszOutput = ValueUnion.psz;
200 break;
201
202 case 'm':
203 {
204 if (cModules >= RT_ELEMENTS(aModules))
205 return RTMsgErrorExitFailure("Too many modules (max %u)", RT_ELEMENTS(aModules));
206 aModules[cModules].pszName = ValueUnion.psz;
207 aModules[cModules].cchName = strlen(ValueUnion.psz);
208
209 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX);
210 if (RT_FAILURE(rc))
211 return RTGetOptPrintError(rc, &ValueUnion); /*??*/
212 aModules[cModules].uAddress = ValueUnion.u64;
213
214 /* We need to find the .text section as that's what we'll be creating an mmap record for. */
215 RTERRINFOSTATIC ErrInfo;
216 RTLDRMOD hLdrMod;
217 rc = RTLdrOpenEx(aModules[cModules].pszName, RTLDR_O_FOR_DEBUG, RTLDRARCH_WHATEVER,
218 &hLdrMod, RTErrInfoInitStatic(&ErrInfo));
219 if (RT_FAILURE(rc))
220 {
221 return RTMsgErrorExitFailure("RTLdrOpenEx failed on '%s': %Rrc%#RTeim",
222 aModules[cModules].pszName, rc, &ErrInfo.Core);
223 }
224 rc = RTLdrEnumSegments(hLdrMod, SegmentEnumCallback, &aModules[cModules]);
225 if (rc != VINF_CALLBACK_RETURN)
226 return RTMsgErrorExitFailure("Failed to locate the .text section in '%s'!", aModules[cModules].pszName);
227
228 aModules[cModules].cbRecord = 0;
229 aModules[cModules].offRecord = UINT64_MAX;
230
231 cModules++;
232 break;
233 }
234
235 case 'q':
236 cVerbosity = 0;
237 break;
238
239 case 'v':
240 cVerbosity++;
241 break;
242
243 case 'h':
244 RTPrintf("usage: %s -i <perf.in> -o <perf.out> -m vmmr0.r0 <loadaddress> [-m ..] [-v]\n"
245 "\n"
246 "It is recommended to use eu-unstrip to combine the VMMR0.r0 and\n"
247 "VMMR0.debug files into a single file again.\n"
248 "\n"
249 "For the 'annotation' feature of perf to work, it is necessary to patch\n"
250 "machine__process_kernel_mmap_event() in tools/perf/utils/machine.c, adding"
251 "the following after 'map->end = map->start + ...:\n"
252 "\n"
253 "/* bird: Transfer pgoff to reloc as dso__process_kernel_symbol overwrites\n"
254 " map->pgoff with sh_offset later. Kind of ASSUMES sh_offset == sh_addr. */\n"
255 "if (event->mmap.pgoff && map->dso && !map->dso->rel)\n"
256 " map->reloc = map->start - event->mmap.pgoff;\n"
257 , argv[0]);
258 return RTEXITCODE_SUCCESS;
259
260 default:
261 return RTGetOptPrintError(ch, &ValueUnion);
262 }
263 }
264 if (!pszInput)
265 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No input file specified");
266 if (!pszOutput)
267 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified");
268 if (RTFileExists(pszOutput))
269 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Output file exists: %s", pszOutput);
270
271
272 /*
273 * Open the input file and check the header.
274 */
275 RTFILE hFile;
276 rc = RTFileOpen(&hFile, pszInput, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
277 if (RT_FAILURE(rc))
278 return RTMsgErrorExitFailure("Failed to open '%s': %Rrc", pszInput, rc);
279
280 union
281 {
282 LNXPERFFILEHDR FileHdr;
283 uint8_t ab[_64K]; /* max record size */
284 } u;
285 rc = RTFileRead(hFile, &u.FileHdr, sizeof(u.FileHdr), NULL);
286 if (RT_FAILURE(rc))
287 return RTMsgErrorExitFailure("Error reading file header: %Rrc", rc);
288 if (u.FileHdr.uMagic != LNXPERFILEHDR_MAGIC)
289 return RTMsgErrorExitFailure("Invalid file header magic: %.8Rhxs", &u.FileHdr.uMagic);
290 if (u.FileHdr.cbHdr != sizeof(u.FileHdr))
291 return RTMsgErrorExitFailure("Invalid file header size: %RU64, expected %zu", u.FileHdr.cbHdr, sizeof(u.FileHdr));
292 uint64_t const offData = u.FileHdr.Data.off;
293 uint64_t const cbData = u.FileHdr.Data.cb;
294
295 /*
296 * Jump to the data portion and look for suitable kmod mmap
297 * records to replace.
298 *
299 * We sort the modules in descreasing name length first to make sure
300 * not to waste voluminous records on short replacement names.
301 */
302 RTSortShell(aModules, cModules, sizeof(aModules[0]), CompModuleNameLengthDesc, NULL);
303
304 unsigned cModulesLeft = cModules ? cModules : cVerbosity > 0;
305 uint64_t offRecord = 0;
306 while (offRecord + 32 < cbData && cModulesLeft > 0)
307 {
308 size_t cbToRead = (size_t)RT_MIN(cbData - offRecord, sizeof(u));
309 rc = RTFileReadAt(hFile, offData + offRecord, &u, cbToRead, NULL);
310 if (RT_FAILURE(rc))
311 return RTMsgErrorExitFailure("RTFileReadAt(,%llu,,%zu,) failed: %Rrc", offData + offRecord, cbToRead, rc);
312
313 uint64_t const offEnd = offRecord + cbToRead;
314 uint8_t *pb = u.ab;
315 while (offRecord + 32 < offEnd)
316 {
317 PLNXPERFRECORDHEADER pRecHdr = (PLNXPERFRECORDHEADER)pb;
318 uint64_t const offNext = offRecord + pRecHdr->cb;
319 if (offNext > offEnd)
320 break;
321 if ( pRecHdr->uType == LNXPERF_RECORD_MMAP
322 && (pRecHdr->fMisc & LNXPERF_RECORD_MISC_CPUMODE_MASK) == LNXPERF_RECORD_MISC_KERNEL)
323 {
324 PLNXPERFRECORDMMAP pMmapRec = (PLNXPERFRECORDMMAP)pRecHdr;
325 if (cVerbosity > 0)
326 RTMsgInfo("MMAP: %016RX64 (%016RX64) LB %012RX64 %s\n",
327 pMmapRec->uAddress, pMmapRec->offFile, pMmapRec->cbMapping, pMmapRec->szFilename);
328
329 bool fSkip = false;
330 for (unsigned i = 0; i < cSkipPatterns && !fSkip; i++)
331 fSkip = RTStrSimplePatternMatch(apszSkipPatterns[i], pMmapRec->szFilename);
332
333 if (!fSkip)
334 {
335 /* Figure the max filename length we dare to put here. */
336 size_t cchFilename = strlen(pMmapRec->szFilename);
337 cchFilename = RT_ALIGN_Z(cchFilename + 1, 8) - 1;
338
339 for (unsigned i = 0; i < cModules; i++)
340 if ( aModules[i].offRecord == UINT64_MAX
341 && aModules[i].cchName <= cchFilename)
342 {
343 aModules[i].cbRecord = pRecHdr->cb;
344 aModules[i].offRecord = offData + offRecord;
345 cModulesLeft--;
346 if (cVerbosity > 0)
347 RTMsgInfo("Will replace module %s at offset %RU64 with %s\n",
348 pMmapRec->szFilename, offRecord, aModules[i].pszName);
349 break;
350 }
351 }
352 }
353
354 /* Advance */
355 pb += pRecHdr->cb;
356 offRecord = offNext;
357 }
358 }
359
360 /*
361 * Only proceed if we found insertion points for all specified modules.
362 */
363 if (cModulesLeft)
364 {
365 if (cModules)
366 {
367 RTMsgError("Unable to find suitable targets for:\n");
368 for (unsigned i = 0; i < cModules; i++)
369 if (aModules[i].offRecord == UINT64_MAX)
370 RTMsgError(" %s\n", aModules[i].pszName);
371 }
372 else
373 RTMsgError("No modules given, so nothing to do.\n");
374 return RTEXITCODE_FAILURE;
375 }
376
377 /*
378 * Sort the modules by record offset to simplify the copying.
379 */
380 RTSortShell(aModules, cModules, sizeof(aModules[0]), CompModuleRecordOffset, NULL);
381
382 RTFILE hOutFile;
383 rc = RTFileOpen(&hOutFile, pszOutput, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE);
384 if (RT_FAILURE(rc))
385 return RTMsgErrorExitFailure("Failed to creating '%s' for the output: %Rrc", pszOutput, rc);
386
387 unsigned iModule = 0;
388 uint64_t offNext = aModules[0].offRecord;
389 uint64_t off = 0;
390 for (;;)
391 {
392 Assert(off <= offNext);
393
394 /* Read a chunk of data. Records we modify are read separately. */
395 size_t cbToRead = RT_MIN(offNext - off, sizeof(u));
396 if (cbToRead == 0)
397 cbToRead = aModules[iModule].cbRecord;
398 size_t cbActual = 0;
399 rc = RTFileReadAt(hFile, off, &u, cbToRead, &cbActual);
400 if (RT_FAILURE(rc))
401 return RTMsgErrorExitFailure("Error reading %zu bytes at %RU64 in '%s': %Rrc", cbToRead, off, pszInput, rc);
402
403 /* EOF? */
404 if (cbActual == 0)
405 break;
406
407 /* A record we wish to modify? */
408 if (off == offNext)
409 {
410 if (cbActual != aModules[iModule].cbRecord)
411 return RTMsgErrorExitFailure("Internal error: cbActual=%zu cbRecord=%u off=%RU64\n",
412 cbActual, aModules[iModule].cbRecord, off);
413
414 PLNXPERFRECORDMMAP pMmapRec = (PLNXPERFRECORDMMAP)&u.ab[0];
415 strcpy(pMmapRec->szFilename, aModules[iModule].pszName);
416 pMmapRec->uAddress = aModules[iModule].uAddress;
417 pMmapRec->cbMapping = aModules[iModule].cbMapping;
418 pMmapRec->offFile = aModules[iModule].offFile;
419 RTMsgInfo("Done: %s\n", pMmapRec->szFilename);
420
421 iModule++;
422 if (iModule < cModules)
423 offNext = aModules[iModule].offRecord;
424 else
425 offNext = UINT64_MAX;
426 }
427
428 /* Write out the data. */
429 rc = RTFileWrite(hOutFile, &u, cbActual, NULL);
430 if (RT_FAILURE(rc))
431 return RTMsgErrorExitFailure("Error writing %zu bytes at %RU64 to '%s': %Rrc", cbActual, off, pszOutput, rc);
432
433 /* Advance.*/
434 off += cbActual;
435 }
436
437 if (iModule != cModules)
438 return RTMsgErrorExitFailure("Internal error: iModule=%u cModules=%u\n", iModule, cModules);
439
440 rc = RTFileClose(hOutFile);
441 if (RT_FAILURE(rc))
442 return RTMsgErrorExitFailure("Error closing output file '%s': %Rrc", pszOutput, rc);
443
444 return RTEXITCODE_SUCCESS;
445}
446
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use