|
1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <iostream> |
|
17 #include <sstream> |
|
18 #include <string> |
|
19 |
|
20 #include <windows.h> |
|
21 |
|
22 #include "utracedecoderapp.h" |
|
23 #include "utraceframe.h" |
|
24 #include "framesorter.h" |
|
25 #include "util.h" |
|
26 #include "logevents\traceeventbase.h" |
|
27 #include "version.h" |
|
28 |
|
29 const int KLogBufferSize = 16384; |
|
30 |
|
31 Parser::CDefinitionParser* gMsgDefParser; |
|
32 bool gQuiet; |
|
33 |
|
34 bool CUTraceDecoderApp::iUseTimes = false; |
|
35 CUTraceDecoderApp::TTimestampPeriod CUTraceDecoderApp::iPeriodType = CUTraceDecoderApp::EUnknown; |
|
36 double CUTraceDecoderApp::iPeriod1; |
|
37 double CUTraceDecoderApp::iPeriod2; |
|
38 |
|
39 CUTraceDecoderApp::CUTraceDecoderApp() |
|
40 { |
|
41 iFrameSorter = new CFrameSorter(*this); |
|
42 |
|
43 iDumpIdentifiers = false; |
|
44 iTesting = false; |
|
45 SetShowContextInfo(true); |
|
46 iCurrentFrame = 0; |
|
47 iUseQuotes = false; |
|
48 iTail = false; |
|
49 |
|
50 iInputBuffer = new unsigned char[KLogBufferSize]; |
|
51 iBufferOffset = 0; |
|
52 iBufferSize = 0; |
|
53 iBufferValid = false; |
|
54 |
|
55 // There will only ever be one CUTraceDecoderApp so this isn't so bad |
|
56 // Needs refactoring though. gMsgDefParser is used by the Node Message Event |
|
57 ::gMsgDefParser = new Parser::CDefinitionParser(); |
|
58 ::gQuiet = false; |
|
59 } |
|
60 |
|
61 |
|
62 CUTraceDecoderApp::~CUTraceDecoderApp() |
|
63 { |
|
64 delete iInputBuffer; |
|
65 } |
|
66 |
|
67 |
|
68 bool CUTraceDecoderApp::Initialise(int aArgc, char **aArgv) |
|
69 { |
|
70 if (aArgc < 2) |
|
71 { |
|
72 return false; |
|
73 } |
|
74 |
|
75 // defaults |
|
76 SetShowContextInfo(true); |
|
77 SetNewLineEnabled(true); |
|
78 |
|
79 int currentArg = 1; |
|
80 while (currentArg < aArgc) |
|
81 { |
|
82 if (aArgv[currentArg][0] == '-' && aArgv[currentArg][1] == '-') |
|
83 { |
|
84 if (!_strcmpi(&aArgv[currentArg][2], "payload-only")) |
|
85 { |
|
86 SetShowContextInfo(false); |
|
87 } |
|
88 else if (!_strcmpi(&aArgv[currentArg][2], "use-quotes")) |
|
89 { |
|
90 iUseQuotes = true; |
|
91 } |
|
92 else if (!_strcmpi(&aArgv[currentArg][2], "use-times")) |
|
93 { |
|
94 iUseTimes = true; |
|
95 } |
|
96 else if (!_strcmpi(&aArgv[currentArg][2], "tail")) |
|
97 { |
|
98 // EXPERIMENTAL: This is an experimental feature at the moment |
|
99 iTail = true; |
|
100 } |
|
101 else if (!_strcmpi(&aArgv[currentArg][2], "quiet")) |
|
102 { |
|
103 ::gQuiet = true; |
|
104 } |
|
105 else if (!_strcmpi(&aArgv[currentArg][2], "message-def")) |
|
106 { |
|
107 currentArg++; |
|
108 iMsgDefFile = aArgv[currentArg]; |
|
109 } |
|
110 else if (!_strcmpi(&aArgv[currentArg][2], "dump-frame")) |
|
111 { |
|
112 currentArg++; |
|
113 char* startFrame = aArgv[currentArg]; |
|
114 |
|
115 char* endFrame = startFrame; |
|
116 while (*endFrame != ',' && *endFrame != 0) |
|
117 { |
|
118 if (*endFrame < '0' || *endFrame > '9') |
|
119 { |
|
120 return false; |
|
121 } |
|
122 ++endFrame; |
|
123 } |
|
124 if (*endFrame == 0) |
|
125 { |
|
126 return false; |
|
127 } |
|
128 *endFrame = 0; |
|
129 if (*startFrame == 0) |
|
130 { |
|
131 return false; |
|
132 } |
|
133 ++endFrame; |
|
134 |
|
135 char* tmp = endFrame; |
|
136 while (*tmp != 0) |
|
137 { |
|
138 if (*tmp < '0' || *tmp > '9') |
|
139 { |
|
140 return false; |
|
141 } |
|
142 ++tmp; |
|
143 } |
|
144 |
|
145 iFrameSorter->SetDumpFrameRange(atol(startFrame), atol(endFrame)); |
|
146 } |
|
147 else if (!_strcmpi(&aArgv[currentArg][2], "dump-event")) |
|
148 { |
|
149 currentArg++; |
|
150 char* startEvent = aArgv[currentArg]; |
|
151 |
|
152 char* endEvent = startEvent; |
|
153 while (*endEvent != ',' && *endEvent != 0) |
|
154 { |
|
155 if (*endEvent < '0' || *endEvent > '9') |
|
156 { |
|
157 return false; |
|
158 } |
|
159 ++endEvent; |
|
160 } |
|
161 if (*endEvent == 0) |
|
162 { |
|
163 return false; |
|
164 } |
|
165 *endEvent = 0; |
|
166 if (*startEvent == 0) |
|
167 { |
|
168 return false; |
|
169 } |
|
170 ++endEvent; |
|
171 |
|
172 char* tmp = endEvent; |
|
173 while (*tmp != 0) |
|
174 { |
|
175 if (*tmp < '0' || *tmp > '9') |
|
176 { |
|
177 return false; |
|
178 } |
|
179 ++tmp; |
|
180 } |
|
181 |
|
182 iFrameSorter->SetDumpEventRange(atol(startEvent), atol(endEvent)); |
|
183 } |
|
184 else if (!_strcmpi(&aArgv[currentArg][2], "dump-message-def")) |
|
185 { |
|
186 iDumpIdentifiers = true; |
|
187 } |
|
188 else if (!_strcmpi(&aArgv[currentArg][2], "test-message-def")) |
|
189 { |
|
190 iTesting = true; |
|
191 } |
|
192 } |
|
193 else |
|
194 { |
|
195 if (iLogFile.length() > 0) |
|
196 { |
|
197 return false; |
|
198 } |
|
199 iLogFile = aArgv[currentArg]; |
|
200 } |
|
201 |
|
202 currentArg++; |
|
203 } |
|
204 |
|
205 if (iMsgDefFile.length() > 0) |
|
206 { |
|
207 if (iTesting) |
|
208 { |
|
209 gMsgDefParser->SetTestMode(); |
|
210 } |
|
211 |
|
212 if (gMsgDefParser->ParseDefinitionFile(iMsgDefFile.c_str()) != Parser::ENoError) |
|
213 { |
|
214 // std::cerr << "Error in message definition file" << std::endl; |
|
215 return false; |
|
216 } |
|
217 } |
|
218 |
|
219 if (iDumpIdentifiers) |
|
220 { |
|
221 gMsgDefParser->DumpIdentifiers(); |
|
222 } |
|
223 |
|
224 if (iTesting) |
|
225 { |
|
226 if (iMsgDefFile.length() == 0) |
|
227 { |
|
228 std::cerr << "No message definition file specifed to check" << std::endl; |
|
229 } |
|
230 return true; |
|
231 } |
|
232 |
|
233 if (iLogFile.length() == 0) |
|
234 { |
|
235 return false; |
|
236 } |
|
237 |
|
238 return true; |
|
239 } |
|
240 |
|
241 |
|
242 void CUTraceDecoderApp::ProcessLog() |
|
243 { |
|
244 // csv header |
|
245 if (ShowContextInfo()) |
|
246 { |
|
247 std::cout << "Sequence,Primary Filter,Sub Category,Time Stamp1,Time Stamp2,Context ID,Description" << std::endl; |
|
248 } |
|
249 |
|
250 // Open the log file and fill the buffer |
|
251 if(!LoadUTraceLog()) |
|
252 { // file couldn't be opened |
|
253 std::cerr << "Error: file " << iLogFile.c_str() << " could not be opened." << std::endl; |
|
254 return; |
|
255 } |
|
256 |
|
257 while (iBufferValid) |
|
258 { |
|
259 while (iBufferOffset < iBufferSize) |
|
260 { |
|
261 //grab a hunk of log |
|
262 int length = iInputBuffer[iBufferOffset]; |
|
263 length = (length + 3) & ~3; |
|
264 |
|
265 // Minimum length is actually 4, but this would mean only the header was |
|
266 // present, so why was it logged |
|
267 if (length < 8 || length > KMaxBTraceRecordSize) |
|
268 { |
|
269 if (!iInputFile.eof()) |
|
270 { |
|
271 std::cerr << "Error: Frame " << iCurrentFrame << " is corrupt - size is out of range (8-116)" << std::endl; |
|
272 |
|
273 // Dump what we have in the buffer, up to the max size of a frame |
|
274 DumpBytes(std::cerr, &iInputBuffer[iBufferOffset], (KMaxBTraceRecordSize < (iBufferSize - iBufferOffset)) ? KMaxBTraceRecordSize : (iBufferSize - iBufferOffset)); |
|
275 } |
|
276 return; |
|
277 } |
|
278 |
|
279 if (length + iBufferOffset > iBufferSize) |
|
280 { |
|
281 break; |
|
282 } |
|
283 |
|
284 CUTraceFrame* frame = new CUTraceFrame(&iInputBuffer[iBufferOffset], iCurrentFrame); |
|
285 if (frame) |
|
286 { |
|
287 iFrameSorter->ProcessFrame(frame); |
|
288 frame = NULL; |
|
289 iCurrentFrame++; |
|
290 } |
|
291 else |
|
292 { |
|
293 std::cerr << "Failed to create frame instance" << std::endl; |
|
294 } |
|
295 |
|
296 iBufferOffset += length; |
|
297 } |
|
298 |
|
299 RefillUTraceBuffer(); |
|
300 } |
|
301 |
|
302 iInputFile.close(); |
|
303 |
|
304 std::cerr << "Processed " << iCurrentFrame << " frames" << std::endl; |
|
305 } |
|
306 |
|
307 |
|
308 unsigned int CUTraceDecoderApp::CurrentFrame() const |
|
309 { |
|
310 return iCurrentFrame; |
|
311 } |
|
312 |
|
313 |
|
314 void CUTraceDecoderApp::WriteEvent(const CTraceEventBase& aEvent) const |
|
315 { |
|
316 if (NewLineEnabled()) |
|
317 { |
|
318 if (ShowContextInfo()) |
|
319 { |
|
320 aEvent.WriteContextInfo(std::cout); |
|
321 } |
|
322 |
|
323 if (iUseQuotes) |
|
324 { |
|
325 std::cout << "\""; |
|
326 } |
|
327 aEvent.WriteEventPrefix(std::cout); |
|
328 } |
|
329 |
|
330 if (iUseQuotes) |
|
331 { |
|
332 std::stringstream ss; |
|
333 aEvent.Describe(ss); |
|
334 std::string s = ss.str(); |
|
335 // todo: replace " with "" |
|
336 std::cout << s; |
|
337 } |
|
338 else |
|
339 { |
|
340 aEvent.Describe(std::cout); |
|
341 } |
|
342 |
|
343 if (NewLineEnabled()) |
|
344 { |
|
345 if (iUseQuotes) |
|
346 { |
|
347 std::cout << "\""; |
|
348 } |
|
349 std::cout << std::endl; |
|
350 } |
|
351 else |
|
352 { |
|
353 std::cout << " "; |
|
354 } |
|
355 }; |
|
356 |
|
357 |
|
358 bool CUTraceDecoderApp::LoadUTraceLog() |
|
359 { |
|
360 iInputFile.open(iLogFile.c_str(), std::ios::in | std::ios::binary); |
|
361 if (iInputFile.is_open()) |
|
362 { |
|
363 RefillUTraceBuffer(); |
|
364 return true; |
|
365 } |
|
366 |
|
367 return false; |
|
368 } |
|
369 |
|
370 |
|
371 void CUTraceDecoderApp::RefillUTraceBuffer() |
|
372 { |
|
373 int bufferSpace = KLogBufferSize; |
|
374 int bufferInUse = 0; |
|
375 |
|
376 if (iBufferOffset) |
|
377 { |
|
378 bufferInUse = iBufferSize - iBufferOffset; |
|
379 bufferSpace = KLogBufferSize - bufferInUse; |
|
380 memcpy(iInputBuffer, &iInputBuffer[iBufferOffset], bufferInUse); |
|
381 } |
|
382 |
|
383 memset(&iInputBuffer[bufferInUse], 0, bufferSpace); |
|
384 iInputFile.read((char*)&iInputBuffer[bufferInUse], bufferSpace); |
|
385 iBufferSize = iInputFile.gcount(); |
|
386 |
|
387 #ifdef _DEBUG |
|
388 std::cerr << std::dec << std::noshowbase << iBufferSize << " bytes read from log file" << std::endl; |
|
389 #endif |
|
390 iBufferValid = (iBufferSize > 0); |
|
391 |
|
392 if (!iBufferValid && iTail) |
|
393 { |
|
394 // EXPERIMENTAL: This is an experimental feature at the moment |
|
395 HANDLE fileChangeHandle; |
|
396 fileChangeHandle = FindFirstChangeNotificationA(iLogFile.c_str(), FALSE, FILE_NOTIFY_CHANGE_SIZE); |
|
397 if (fileChangeHandle != INVALID_HANDLE_VALUE) |
|
398 { |
|
399 iInputFile.read((char*)&iInputBuffer[bufferInUse], bufferSpace); |
|
400 iBufferSize = iInputFile.gcount(); |
|
401 |
|
402 std::cerr << std::dec << std::noshowbase << iBufferSize << " bytes read from log file" << std::endl; |
|
403 iBufferValid = (iBufferSize > 0); |
|
404 FindCloseChangeNotification(fileChangeHandle); |
|
405 } |
|
406 } |
|
407 |
|
408 iBufferSize += bufferInUse; |
|
409 iBufferOffset = 0; |
|
410 } |
|
411 |
|
412 |
|
413 void CUTraceDecoderApp::ShowUsage() |
|
414 { |
|
415 std::cerr |
|
416 << "utracedecoder " APPVERSION ":" << std::endl |
|
417 << "Log parser for utrace logs, converts a utrace generated log file into human readable and perl processable csv format" << std::endl |
|
418 << "utracedecoder [options] <path_to_log>" << std::endl |
|
419 << " --payload-only Show only the message part of the trace" << std::endl |
|
420 << " --use-quotes Wrap the description in quotes" << std::endl |
|
421 << " --use-times \"time-in-seconds,0\" substituted for \"timestamp1,timestamp\" pair" << std::endl |
|
422 << " --message-def <message_definition_file> The file from which to load message and signature definition" << std::endl |
|
423 << " --test-message-def Tests the message definition file showing all errors then exits" << std::endl |
|
424 << std::endl << "Debugging options" << std::endl |
|
425 << " --dump-message-def Dumps the parsed definition file details" << std::endl |
|
426 << " --dump-frame <start_frame>,<end_frame> Dumps the frames in the given range to stderr" << std::endl |
|
427 << " --dump-event <first_event>,<last_event> Dumps the events in the given range to stderr" << std::endl |
|
428 << " --quiet Only output urgent warnings" << std::endl |
|
429 << std::endl; |
|
430 } |
|
431 |
|
432 void CUTraceDecoderApp::SetTimestampPeriod(CUTraceDecoderApp::TTimestampPeriod aType, double aPeriod1, double aPeriod2) |
|
433 { |
|
434 iPeriodType = aType; |
|
435 iPeriod1 = aPeriod1; |
|
436 iPeriod2 = aPeriod2; |
|
437 } |
|
438 |