VirtualBox

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

Last change on this file since 87644 was 87644, checked in by vboxsync, 4 years ago

SUP/linux: Quick hack for getting some VMMR0.r0 info out of 'perf report'.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette