VirtualBox

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

Last change on this file since 74071 was 71492, checked in by vboxsync, 7 years ago

Runtime: Introduce RTTraceLog* API for creating and parsing binary trace log files. The format and API is designed with the

following goals in mind:

  1. Allow streaming the data via network or write it to a file.
  2. Embed the structure of the traced data into the log allowing arbitrary structured data to be embedded without requiring adaptions on the parsing side.
  3. Allow grouping of traced data belonging together to easily follow chains of events later on.
  4. Trace events can have a parent assigned to check where an event originally originated from.
  5. Low overhead on the creation side.

The current state implements the absolute basics on the creation and parsing side and is work in progress. This will
be used later on in the device emulation fuzzer to capture device and examine device states and changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.1 KB
Line 
1/* $Id: RTTraceLogTool.cpp 71492 2018-03-24 22:23:10Z vboxsync $ */
2/** @file
3 * IPRT - Utility for reading/receiving and dissecting trace logs.
4 */
5
6/*
7 * Copyright (C) 2018 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/tracelog.h>
32
33#include <iprt/assert.h>
34#include <iprt/err.h>
35#include <iprt/getopt.h>
36#include <iprt/initterm.h>
37#include <iprt/message.h>
38#include <iprt/mem.h>
39#include <iprt/path.h>
40#include <iprt/stream.h>
41#include <iprt/string.h>
42#include <iprt/tcp.h>
43
44
45/**
46 * The tracelog tool TCP server/client state.
47 */
48typedef struct RTTRACELOGTOOLTCP
49{
50 /** Flag whether this is a server. */
51 bool fIsServer;
52 /** The TCP socket handle for the connection. */
53 RTSOCKET hSock;
54 /** The TCP server. */
55 PRTTCPSERVER pTcpSrv;
56} RTTRACELOGTOOLTCP;
57/** Pointer to the TCP server/client state. */
58typedef RTTRACELOGTOOLTCP *PRTTRACELOGTOOLTCP;
59
60
61static void rtTraceLogTcpDestroy(PRTTRACELOGTOOLTCP pTrcLogTcp)
62{
63 if (pTrcLogTcp->fIsServer)
64 RTTcpServerDestroy(pTrcLogTcp->pTcpSrv);
65 if (pTrcLogTcp->hSock != NIL_RTSOCKET)
66 {
67 if (pTrcLogTcp->fIsServer)
68 RTTcpServerDisconnectClient2(pTrcLogTcp->hSock);
69 else
70 RTTcpClientClose(pTrcLogTcp->hSock);
71 }
72 RTMemFree(pTrcLogTcp);
73}
74
75
76static DECLCALLBACK(int) rtTraceLogToolTcpInput(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbRead,
77 RTMSINTERVAL cMsTimeout)
78{
79 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)pvUser;
80 if ( pTrcLogTcp->fIsServer
81 && pTrcLogTcp->hSock == NIL_RTSOCKET)
82 {
83 int rc = RTTcpServerListen2(pTrcLogTcp->pTcpSrv, &pTrcLogTcp->hSock);
84 if (RT_FAILURE(rc))
85 return rc;
86 }
87
88 int rc = RTTcpSelectOne(pTrcLogTcp->hSock, cMsTimeout);
89 if (RT_SUCCESS(rc))
90 rc = RTTcpReadNB(pTrcLogTcp->hSock, pvBuf, cbBuf, pcbRead);
91
92 return rc;
93}
94
95
96static DECLCALLBACK(int) rtTraceLogToolTcpClose(void *pvUser)
97{
98 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)pvUser;
99 rtTraceLogTcpDestroy(pTrcLogTcp);
100 return VINF_SUCCESS;
101}
102
103
104/**
105 * Tries to create a new trace log reader using the given input.
106 *
107 * @returns IPRT status code.
108 * @param phTraceLogRdr Where to store the handle to the trace log reader instance on success.
109 * @param pszInput The input path.
110 * @param pszSave The optional path to save
111 */
112static int rtTraceLogToolReaderCreate(PRTTRACELOGRDR phTraceLogRdr, const char *pszInput, const char *pszSave)
113{
114 RT_NOREF(pszSave);
115
116 /* Try treating the input as a file first. */
117 int rc = RTTraceLogRdrCreateFromFile(phTraceLogRdr, pszInput);
118 if (RT_FAILURE(rc))
119 {
120 /*
121 * Check whether the input looks like a port number or an address:port pair.
122 * The former will create a server listening on the port while the latter tries
123 * to connect to the given address:port combination.
124 */
125 uint32_t uPort = 0;
126 bool fIsServer = false;
127 PRTTCPSERVER pTcpSrv = NULL;
128 RTSOCKET hSock = NIL_RTSOCKET;
129 rc = RTStrToUInt32Full(pszInput, 10, &uPort);
130 if (rc == VINF_SUCCESS)
131 {
132 fIsServer = true;
133 rc = RTTcpServerCreateEx(NULL, uPort, &pTcpSrv);
134 }
135 else
136 {
137 /* Try treating the input as an address:port pair. */
138 }
139
140 if (RT_SUCCESS(rc))
141 {
142 /* Initialize structure and reader. */
143 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)RTMemAllocZ(sizeof(*pTrcLogTcp));
144 if (pTrcLogTcp)
145 {
146 pTrcLogTcp->fIsServer = fIsServer;
147 pTrcLogTcp->hSock = hSock;
148 pTrcLogTcp->pTcpSrv = pTcpSrv;
149 rc = RTTraceLogRdrCreate(phTraceLogRdr, rtTraceLogToolTcpInput, rtTraceLogToolTcpClose, pTrcLogTcp);
150 if (RT_FAILURE(rc))
151 rtTraceLogTcpDestroy(pTrcLogTcp);
152 }
153 else
154 {
155 if (fIsServer)
156 RTTcpServerDestroy(pTcpSrv);
157 else
158 RTSocketClose(hSock);
159 }
160 }
161 }
162 return rc;
163}
164
165
166int main(int argc, char **argv)
167{
168 int rc = RTR3InitExe(argc, &argv, 0);
169 if (RT_FAILURE(rc))
170 return RTMsgInitFailure(rc);
171
172 /*
173 * Parse arguments.
174 */
175 static const RTGETOPTDEF s_aOptions[] =
176 {
177 { "--input", 'i', RTGETOPT_REQ_STRING },
178 { "--save", 's', RTGETOPT_REQ_STRING },
179 { "--help", 'h', RTGETOPT_REQ_NOTHING },
180 { "--version", 'V', RTGETOPT_REQ_NOTHING },
181 };
182
183 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
184 const char *pszInput = NULL;
185 const char *pszSave = NULL;
186
187 RTGETOPTUNION ValueUnion;
188 RTGETOPTSTATE GetState;
189 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
190 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
191 {
192 switch (rc)
193 {
194 case 'h':
195 RTPrintf("Usage: %s [options]\n"
196 "\n"
197 "Options:\n"
198 " -i,--input=<file|port|address:port>\n"
199 " Input path, can be a file a port to start listening on for incoming connections or an address:port to connect to\n"
200 " -s,--save=file\n"
201 " Save the input to a file for later use\n"
202 " -h, -?, --help\n"
203 " Display this help text and exit successfully.\n"
204 " -V, --version\n"
205 " Display the revision and exit successfully.\n"
206 , RTPathFilename(argv[0]));
207 return RTEXITCODE_SUCCESS;
208 case 'V':
209 RTPrintf("$Revision: 71492 $\n");
210 return RTEXITCODE_SUCCESS;
211
212 case 'i':
213 pszInput = ValueUnion.psz;
214 break;
215 case 's':
216 pszSave = ValueUnion.psz;
217 break;
218 default:
219 return RTGetOptPrintError(rc, &ValueUnion);
220 }
221 }
222
223 if (!pszInput)
224 {
225 RTPrintf("An input path must be given\n");
226 return RTEXITCODE_FAILURE;
227 }
228
229 /*
230 * Create trace log reader instance.
231 */
232 RTTRACELOGRDR hTraceLogRdr = NIL_RTTRACELOGRDR;
233 rc = rtTraceLogToolReaderCreate(&hTraceLogRdr, pszInput, pszSave);
234 if (RT_SUCCESS(rc))
235 {
236 do
237 {
238 RTTRACELOGRDRPOLLEVT enmEvt = RTTRACELOGRDRPOLLEVT_INVALID;
239 rc = RTTraceLogRdrEvtPoll(hTraceLogRdr, &enmEvt, RT_INDEFINITE_WAIT);
240 if (RT_SUCCESS(rc))
241 {
242 switch (enmEvt)
243 {
244 case RTTRACELOGRDRPOLLEVT_HDR_RECVD:
245 RTMsgInfo("A valid header was received\n");
246 break;
247 case RTTRACELOGRDRPOLLEVT_TRACE_EVENT_RECVD:
248 {
249 RTTRACELOGRDREVT hTraceLogEvt;
250 rc = RTTraceLogRdrQueryLastEvt(hTraceLogRdr, &hTraceLogEvt);
251 if (RT_SUCCESS(rc))
252 {
253 PCRTTRACELOGEVTDESC pEvtDesc = RTTraceLogRdrEvtGetDesc(hTraceLogEvt);
254 RTMsgInfo("%llu %llu %s\n",
255 RTTraceLogRdrEvtGetSeqNo(hTraceLogEvt),
256 RTTraceLogRdrEvtGetTs(hTraceLogEvt),
257 pEvtDesc->pszId);
258 for (unsigned i = 0; i < pEvtDesc->cEvtItems; i++)
259 {
260 RTTRACELOGEVTVAL Val;
261 unsigned cVals = 0;
262 rc = RTTraceLogRdrEvtFillVals(hTraceLogEvt, i, &Val, 1, &cVals);
263 if (RT_SUCCESS(rc))
264 {
265 switch (Val.pItemDesc->enmType)
266 {
267 case RTTRACELOGTYPE_BOOL:
268 RTMsgInfo(" %s: %s\n", Val.pItemDesc->pszName, Val.u.f ? "true" : "false");
269 break;
270 case RTTRACELOGTYPE_UINT8:
271 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u8);
272 break;
273 case RTTRACELOGTYPE_INT8:
274 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i8);
275 break;
276 case RTTRACELOGTYPE_UINT16:
277 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u16);
278 break;
279 case RTTRACELOGTYPE_INT16:
280 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i16);
281 break;
282 case RTTRACELOGTYPE_UINT32:
283 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u32);
284 break;
285 case RTTRACELOGTYPE_INT32:
286 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i32);
287 break;
288 case RTTRACELOGTYPE_UINT64:
289 RTMsgInfo(" %s: %llu\n", Val.pItemDesc->pszName, Val.u.u64);
290 break;
291 case RTTRACELOGTYPE_INT64:
292 RTMsgInfo(" %s: %lld\n", Val.pItemDesc->pszName, Val.u.i64);
293 break;
294 case RTTRACELOGTYPE_FLOAT32:
295 case RTTRACELOGTYPE_FLOAT64:
296 case RTTRACELOGTYPE_RAWDATA:
297 RTMsgInfo(" %s: Float32, Float64 and raw data not supported yet\n", Val.pItemDesc->pszName);
298 break;
299 case RTTRACELOGTYPE_POINTER:
300 RTMsgInfo(" %s: %#llx\n", Val.pItemDesc->pszName, Val.u.uPtr);
301 break;
302 case RTTRACELOGTYPE_SIZE:
303 RTMsgInfo(" %s: %llu\n", Val.pItemDesc->pszName, Val.u.sz);
304 break;
305 default:
306 RTMsgError(" %s: Invalid type given %d\n", Val.pItemDesc->pszName, Val.pItemDesc->enmType);
307 }
308 }
309 else
310 RTMsgInfo(" Failed to retrieve event data with %Rrc\n", rc);
311 }
312 }
313 break;
314 }
315 default:
316 RTMsgInfo("Invalid event received: %d\n", enmEvt);
317 }
318 }
319 else
320 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Polling for an event failed with %Rrc\n", rc);
321 } while (RT_SUCCESS(rc));
322
323 RTTraceLogRdrDestroy(hTraceLogRdr);
324 }
325 else
326 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create trace log reader with %Rrc\n", rc);
327
328 return rcExit;
329}
330
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