VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTTraceLogTool.cpp@ 104892

最後變更 在這個檔案從104892是 104892,由 vboxsync 提交於 9 月 前

Runtime/RTTraceLogTool: Add support for loading decoder plugins which can do further analysis on trace log events, bugref:10701

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 18.8 KB
 
1/* $Id: RTTraceLogTool.cpp 104892 2024-06-12 13:51:26Z vboxsync $ */
2/** @file
3 * IPRT - Utility for reading/receiving and dissecting trace logs.
4 */
5
6/*
7 * Copyright (C) 2018-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.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/tracelog.h>
42#include <iprt/tracelog-decoder-plugin.h>
43
44#include <iprt/assert.h>
45#include <iprt/errcore.h>
46#include <iprt/getopt.h>
47#include <iprt/initterm.h>
48#include <iprt/ldr.h>
49#include <iprt/message.h>
50#include <iprt/mem.h>
51#include <iprt/path.h>
52#include <iprt/stream.h>
53#include <iprt/string.h>
54#include <iprt/tcp.h>
55
56
57/**
58 * Loaded tracelog decoders.
59 */
60typedef struct RTTRACELOGDECODERS
61{
62 /** Pointer to the array of registered decoders. */
63 PRTTRACELOGDECODERDECODEEVENT paDecodeEvts;
64 /** Number of entries in the decoder array. */
65 uint32_t cDecoders;
66 /** Allocation size of the decoder array. */
67 uint32_t cDecodersAlloc;
68} RTTRACELOGDECODERS;
69typedef RTTRACELOGDECODERS *PRTTRACELOGDECODERS;
70
71
72/**
73 * The tracelog tool TCP server/client state.
74 */
75typedef struct RTTRACELOGTOOLTCP
76{
77 /** Flag whether this is a server. */
78 bool fIsServer;
79 /** The TCP socket handle for the connection. */
80 RTSOCKET hSock;
81 /** The TCP server. */
82 PRTTCPSERVER pTcpSrv;
83} RTTRACELOGTOOLTCP;
84/** Pointer to the TCP server/client state. */
85typedef RTTRACELOGTOOLTCP *PRTTRACELOGTOOLTCP;
86
87
88static void rtTraceLogTcpDestroy(PRTTRACELOGTOOLTCP pTrcLogTcp)
89{
90 if (pTrcLogTcp->fIsServer)
91 RTTcpServerDestroy(pTrcLogTcp->pTcpSrv);
92 if (pTrcLogTcp->hSock != NIL_RTSOCKET)
93 {
94 if (pTrcLogTcp->fIsServer)
95 RTTcpServerDisconnectClient2(pTrcLogTcp->hSock);
96 else
97 RTTcpClientClose(pTrcLogTcp->hSock);
98 }
99 RTMemFree(pTrcLogTcp);
100}
101
102
103static DECLCALLBACK(int) rtTraceLogToolTcpInput(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbRead,
104 RTMSINTERVAL cMsTimeout)
105{
106 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)pvUser;
107 if ( pTrcLogTcp->fIsServer
108 && pTrcLogTcp->hSock == NIL_RTSOCKET)
109 {
110 int rc = RTTcpServerListen2(pTrcLogTcp->pTcpSrv, &pTrcLogTcp->hSock);
111 if (RT_FAILURE(rc))
112 return rc;
113 }
114
115 int rc = RTTcpSelectOne(pTrcLogTcp->hSock, cMsTimeout);
116 if (RT_SUCCESS(rc))
117 rc = RTTcpReadNB(pTrcLogTcp->hSock, pvBuf, cbBuf, pcbRead);
118
119 return rc;
120}
121
122
123static DECLCALLBACK(int) rtTraceLogToolTcpClose(void *pvUser)
124{
125 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)pvUser;
126 rtTraceLogTcpDestroy(pTrcLogTcp);
127 return VINF_SUCCESS;
128}
129
130
131/**
132 * Tries to create a new trace log reader using the given input.
133 *
134 * @returns IPRT status code.
135 * @param phTraceLogRdr Where to store the handle to the trace log reader instance on success.
136 * @param pszInput The input path.
137 * @param pszSave The optional path to save
138 */
139static int rtTraceLogToolReaderCreate(PRTTRACELOGRDR phTraceLogRdr, const char *pszInput, const char *pszSave)
140{
141 RT_NOREF(pszSave);
142
143 /* Try treating the input as a file first. */
144 int rc = RTTraceLogRdrCreateFromFile(phTraceLogRdr, pszInput);
145 if (RT_FAILURE(rc))
146 {
147 /*
148 * Check whether the input looks like a port number or an address:port pair.
149 * The former will create a server listening on the port while the latter tries
150 * to connect to the given address:port combination.
151 */
152 uint32_t uPort = 0;
153 bool fIsServer = false;
154 PRTTCPSERVER pTcpSrv = NULL;
155 RTSOCKET hSock = NIL_RTSOCKET;
156 rc = RTStrToUInt32Full(pszInput, 10, &uPort);
157 if (rc == VINF_SUCCESS)
158 {
159 fIsServer = true;
160 rc = RTTcpServerCreateEx(NULL, uPort, &pTcpSrv);
161 }
162 else
163 {
164 /* Try treating the input as an address:port pair. */
165 }
166
167 if (RT_SUCCESS(rc))
168 {
169 /* Initialize structure and reader. */
170 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)RTMemAllocZ(sizeof(*pTrcLogTcp));
171 if (pTrcLogTcp)
172 {
173 pTrcLogTcp->fIsServer = fIsServer;
174 pTrcLogTcp->hSock = hSock;
175 pTrcLogTcp->pTcpSrv = pTcpSrv;
176 rc = RTTraceLogRdrCreate(phTraceLogRdr, rtTraceLogToolTcpInput, rtTraceLogToolTcpClose, pTrcLogTcp);
177 if (RT_FAILURE(rc))
178 rtTraceLogTcpDestroy(pTrcLogTcp);
179 }
180 else
181 {
182 if (fIsServer)
183 RTTcpServerDestroy(pTcpSrv);
184 else
185 RTSocketClose(hSock);
186 }
187 }
188 }
189 return rc;
190}
191
192
193static DECLCALLBACK(int) rtTraceLogToolRegisterDecoders(void *pvUser, PCRTTRACELOGDECODERDECODEEVENT paDecoders, uint32_t cDecoders)
194{
195 PRTTRACELOGDECODERS pDecoderState = (PRTTRACELOGDECODERS)pvUser;
196
197 if (pDecoderState->cDecodersAlloc - pDecoderState->cDecoders <= cDecoders)
198 {
199 PRTTRACELOGDECODERDECODEEVENT paNew = (PRTTRACELOGDECODERDECODEEVENT)RTMemRealloc(pDecoderState->paDecodeEvts,
200 (pDecoderState->cDecodersAlloc + cDecoders) * sizeof(*paDecoders));
201 if (!paNew)
202 return VERR_NO_MEMORY;
203
204 pDecoderState->paDecodeEvts = paNew;
205 pDecoderState->cDecodersAlloc += cDecoders;
206 }
207
208 memcpy(&pDecoderState->paDecodeEvts[pDecoderState->cDecoders], paDecoders, cDecoders * sizeof(*paDecoders));
209 pDecoderState->cDecoders += cDecoders;
210 return VINF_SUCCESS;
211}
212
213
214int main(int argc, char **argv)
215{
216 int rc = RTR3InitExe(argc, &argv, 0);
217 if (RT_FAILURE(rc))
218 return RTMsgInitFailure(rc);
219
220 /*
221 * Parse arguments.
222 */
223 static const RTGETOPTDEF s_aOptions[] =
224 {
225 { "--input", 'i', RTGETOPT_REQ_STRING },
226 { "--save", 's', RTGETOPT_REQ_STRING },
227 { "--load-decoder", 'l', RTGETOPT_REQ_STRING },
228 { "--help", 'h', RTGETOPT_REQ_NOTHING },
229 { "--version", 'V', RTGETOPT_REQ_NOTHING },
230 };
231
232 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
233 const char *pszInput = NULL;
234 const char *pszSave = NULL;
235 RTTRACELOGDECODERS Decoders; RT_ZERO(Decoders);
236
237 RTGETOPTUNION ValueUnion;
238 RTGETOPTSTATE GetState;
239 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
240 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
241 {
242 switch (rc)
243 {
244 case 'h':
245 RTPrintf("Usage: %s [options]\n"
246 "\n"
247 "Options:\n"
248 " -i,--input=<file|port|address:port>\n"
249 " Input path, can be a file a port to start listening on for incoming connections or an address:port to connect to\n"
250 " -s,--save=file\n"
251 " Save the input to a file for later use\n"
252 " -l,--load-decoder=<plugin path>\n"
253 " Loads the given decoder library used for decoding events\n"
254 " -h, -?, --help\n"
255 " Display this help text and exit successfully.\n"
256 " -V, --version\n"
257 " Display the revision and exit successfully.\n"
258 , RTPathFilename(argv[0]));
259 return RTEXITCODE_SUCCESS;
260 case 'V':
261 RTPrintf("$Revision: 104892 $\n");
262 return RTEXITCODE_SUCCESS;
263
264 case 'i':
265 pszInput = ValueUnion.psz;
266 break;
267 case 's':
268 pszSave = ValueUnion.psz;
269 break;
270 case 'l':
271 {
272 RTLDRMOD hLdrMod;
273 rc = RTLdrLoadEx(ValueUnion.psz, &hLdrMod, RTLDRLOAD_FLAGS_NO_UNLOAD, NULL);
274 if (RT_SUCCESS(rc))
275 {
276 PFNTRACELOGDECODERPLUGINLOAD pfnLoad = NULL;
277 rc = RTLdrGetSymbol(hLdrMod, RT_TRACELOG_DECODER_PLUGIN_LOAD, (void **)&pfnLoad);
278 if (RT_SUCCESS(rc))
279 {
280 RTTRACELOGDECODERREGISTER RegCb;
281
282 RegCb.u32Version = RT_TRACELOG_DECODERREG_CB_VERSION;
283 RegCb.pfnRegisterDecoders = rtTraceLogToolRegisterDecoders;
284
285 rc = pfnLoad(&Decoders, &RegCb);
286 if (RT_FAILURE(rc))
287 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to register decoders %Rrc\n", rc);
288 }
289 else
290 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to lretrieve entry point '%s' %Rrc\n",
291 RT_TRACELOG_DECODER_PLUGIN_LOAD, rc);
292
293 RTLdrClose(hLdrMod);
294 }
295 else
296 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to load decoder library %Rrc\n", rc);
297 break;
298 }
299 default:
300 return RTGetOptPrintError(rc, &ValueUnion);
301 }
302 }
303
304 if (!pszInput)
305 {
306 RTPrintf("An input path must be given\n");
307 return RTEXITCODE_FAILURE;
308 }
309
310 /*
311 * Create trace log reader instance.
312 */
313 RTTRACELOGRDR hTraceLogRdr = NIL_RTTRACELOGRDR;
314 rc = rtTraceLogToolReaderCreate(&hTraceLogRdr, pszInput, pszSave);
315 if (RT_SUCCESS(rc))
316 {
317 do
318 {
319 RTTRACELOGRDRPOLLEVT enmEvt = RTTRACELOGRDRPOLLEVT_INVALID;
320 rc = RTTraceLogRdrEvtPoll(hTraceLogRdr, &enmEvt, RT_INDEFINITE_WAIT);
321 if (RT_SUCCESS(rc))
322 {
323 switch (enmEvt)
324 {
325 case RTTRACELOGRDRPOLLEVT_HDR_RECVD:
326 RTMsgInfo("A valid header was received\n");
327 break;
328 case RTTRACELOGRDRPOLLEVT_TRACE_EVENT_RECVD:
329 {
330 RTTRACELOGRDREVT hTraceLogEvt;
331 rc = RTTraceLogRdrQueryLastEvt(hTraceLogRdr, &hTraceLogEvt);
332 if (RT_SUCCESS(rc))
333 {
334 PCRTTRACELOGEVTDESC pEvtDesc = RTTraceLogRdrEvtGetDesc(hTraceLogEvt);
335 RTMsgInfo("%llu %llu %s\n",
336 RTTraceLogRdrEvtGetSeqNo(hTraceLogEvt),
337 RTTraceLogRdrEvtGetTs(hTraceLogEvt),
338 pEvtDesc->pszId);
339
340 /*
341 * Look through our registered decoders and pass the decoding on to it.
342 * If there is no decoder registered just dump the raw values.
343 */
344 PCRTTRACELOGDECODERDECODEEVENT pDecodeEvt = NULL;
345 for (uint32_t i = 0; i < Decoders.cDecoders; i++)
346 if (!strcmp(Decoders.paDecodeEvts[i].pszId, pEvtDesc->pszId))
347 {
348 pDecodeEvt = &Decoders.paDecodeEvts[i];
349 break;
350 }
351
352 if (pDecodeEvt)
353 {
354 /** @todo Dynamic value allocation (too lazy right now). */
355 RTTRACELOGEVTVAL aVals[32];
356 uint32_t cVals = 0;
357 rc = RTTraceLogRdrEvtFillVals(hTraceLogEvt, 0, &aVals[0], RT_ELEMENTS(aVals),
358 &cVals);
359 if ( RT_SUCCESS(rc)
360 || cVals != pEvtDesc->cEvtItems)
361 {
362 rc = pDecodeEvt->pfnDecode(hTraceLogEvt, pEvtDesc, &aVals[0], cVals);
363 if (RT_FAILURE(rc))
364 RTMsgError("Failed to decode event with ID '%s' -> %Rrc\n", pEvtDesc->pszId, rc);
365 }
366 else
367 RTMsgError("Failed to fill values for event with ID '%s' -> %Rrc (cVals=%u vs. cEvtItems=%u)\n",
368 pEvtDesc->pszId, rc, cVals, pEvtDesc->cEvtItems);
369 }
370 else
371 for (unsigned i = 0; i < pEvtDesc->cEvtItems; i++)
372 {
373 RTTRACELOGEVTVAL Val;
374 unsigned cVals = 0;
375 rc = RTTraceLogRdrEvtFillVals(hTraceLogEvt, i, &Val, 1, &cVals);
376 if (RT_SUCCESS(rc))
377 {
378 switch (Val.pItemDesc->enmType)
379 {
380 case RTTRACELOGTYPE_BOOL:
381 RTMsgInfo(" %s: %s\n", Val.pItemDesc->pszName, Val.u.f ? "true" : "false");
382 break;
383 case RTTRACELOGTYPE_UINT8:
384 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u8);
385 break;
386 case RTTRACELOGTYPE_INT8:
387 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i8);
388 break;
389 case RTTRACELOGTYPE_UINT16:
390 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u16);
391 break;
392 case RTTRACELOGTYPE_INT16:
393 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i16);
394 break;
395 case RTTRACELOGTYPE_UINT32:
396 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u32);
397 break;
398 case RTTRACELOGTYPE_INT32:
399 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i32);
400 break;
401 case RTTRACELOGTYPE_UINT64:
402 RTMsgInfo(" %s: %llu\n", Val.pItemDesc->pszName, Val.u.u64);
403 break;
404 case RTTRACELOGTYPE_INT64:
405 RTMsgInfo(" %s: %lld\n", Val.pItemDesc->pszName, Val.u.i64);
406 break;
407 case RTTRACELOGTYPE_RAWDATA:
408 RTMsgInfo(" %s:\n"
409 "%.*Rhxd\n", Val.pItemDesc->pszName, Val.u.RawData.cb, Val.u.RawData.pb);
410 break;
411 case RTTRACELOGTYPE_FLOAT32:
412 case RTTRACELOGTYPE_FLOAT64:
413 RTMsgInfo(" %s: Float32 and Float64 data not supported yet\n", Val.pItemDesc->pszName);
414 break;
415 case RTTRACELOGTYPE_POINTER:
416 RTMsgInfo(" %s: %#llx\n", Val.pItemDesc->pszName, Val.u.uPtr);
417 break;
418 case RTTRACELOGTYPE_SIZE:
419 RTMsgInfo(" %s: %llu\n", Val.pItemDesc->pszName, Val.u.sz);
420 break;
421 default:
422 RTMsgError(" %s: Invalid type given %d\n", Val.pItemDesc->pszName, Val.pItemDesc->enmType);
423 }
424 }
425 else
426 RTMsgInfo(" Failed to retrieve event data with %Rrc\n", rc);
427 }
428 }
429 break;
430 }
431 default:
432 RTMsgInfo("Invalid event received: %d\n", enmEvt);
433 }
434 }
435 else
436 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Polling for an event failed with %Rrc\n", rc);
437 } while (RT_SUCCESS(rc));
438
439 RTTraceLogRdrDestroy(hTraceLogRdr);
440 }
441 else
442 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create trace log reader with %Rrc\n", rc);
443
444 return rcExit;
445}
446
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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