|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the tools applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "tcftrkmessage.h" |
|
43 #include "json.h" |
|
44 |
|
45 #include <QtCore/QString> |
|
46 #include <QtCore/QTextStream> |
|
47 |
|
48 // Names matching the enum |
|
49 static const char *serviceNamesC[] = |
|
50 { "Locator", "RunControl", "Processes", "Memory", "Settings", "Breakpoints", |
|
51 "Registers", "SimpleRegisters", |
|
52 "UnknownService"}; |
|
53 |
|
54 namespace tcftrk { |
|
55 |
|
56 SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep) |
|
57 { |
|
58 QString rc; |
|
59 const int count = a.size(); |
|
60 for (int i = 0; i < count; i++) { |
|
61 if (i) |
|
62 rc += QLatin1Char(sep); |
|
63 rc += QString::fromUtf8(a.at(i)); |
|
64 } |
|
65 return rc; |
|
66 } |
|
67 |
|
68 static inline bool jsonToBool(const JsonValue& js) |
|
69 { |
|
70 return js.type() == JsonValue::Boolean && js.data() == "true"; |
|
71 } |
|
72 |
|
73 SYMBIANUTILS_EXPORT const char *serviceName(Services s) |
|
74 { |
|
75 return serviceNamesC[s]; |
|
76 } |
|
77 |
|
78 SYMBIANUTILS_EXPORT Services serviceFromName(const char *n) |
|
79 { |
|
80 const int count = sizeof(serviceNamesC)/sizeof(char *); |
|
81 for (int i = 0; i < count; i++) |
|
82 if (!qstrcmp(serviceNamesC[i], n)) |
|
83 return static_cast<Services>(i); |
|
84 return UnknownService; |
|
85 } |
|
86 |
|
87 SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a) |
|
88 { |
|
89 const int columns = 16; |
|
90 QString rc; |
|
91 QTextStream str(&rc); |
|
92 str.setIntegerBase(16); |
|
93 str.setPadChar(QLatin1Char('0')); |
|
94 const unsigned char *start = reinterpret_cast<const unsigned char *>(a.constData()); |
|
95 const unsigned char *end = start + a.size(); |
|
96 for (const unsigned char *p = start; p < end ; ) { |
|
97 str << "0x"; |
|
98 str.setFieldWidth(4); |
|
99 str << (p - start); |
|
100 str.setFieldWidth(0); |
|
101 str << ' '; |
|
102 QString asc; |
|
103 int c = 0; |
|
104 for ( ; c < columns && p < end; c++, p++) { |
|
105 const unsigned u = *p; |
|
106 str.setFieldWidth(2); |
|
107 str << u; |
|
108 str.setFieldWidth(0); |
|
109 str << ' '; |
|
110 switch (u) { |
|
111 case '\n': |
|
112 asc += QLatin1String("\\n"); |
|
113 break; |
|
114 case '\r': |
|
115 asc += QLatin1String("\\r"); |
|
116 break; |
|
117 case '\t': |
|
118 asc += QLatin1String("\\t"); |
|
119 break; |
|
120 default: |
|
121 if (u >= 32 && u < 128) { |
|
122 asc += QLatin1Char(' '); |
|
123 asc += QLatin1Char(u); |
|
124 } else { |
|
125 asc += QLatin1String(" ."); |
|
126 } |
|
127 break; |
|
128 } |
|
129 } |
|
130 if (const int remainder = columns - c) |
|
131 str << QString(3 * remainder, QLatin1Char(' ')); |
|
132 str << ' ' << asc << '\n'; |
|
133 } |
|
134 return rc; |
|
135 } |
|
136 |
|
137 // ----------- RunControlContext |
|
138 RunControlContext::RunControlContext() : |
|
139 flags(0), resumeFlags(0) |
|
140 { |
|
141 } |
|
142 |
|
143 void RunControlContext::clear() |
|
144 { |
|
145 flags =0; |
|
146 resumeFlags = 0; |
|
147 id.clear(); |
|
148 osid.clear(); |
|
149 parentId.clear(); |
|
150 } |
|
151 |
|
152 RunControlContext::Type RunControlContext::typeFromTcfId(const QByteArray &id) |
|
153 { |
|
154 // "p12" or "p12.t34"? |
|
155 return id.contains(".t") ? Thread : Process; |
|
156 } |
|
157 |
|
158 unsigned RunControlContext::processId() const |
|
159 { |
|
160 return processIdFromTcdfId(id); |
|
161 } |
|
162 |
|
163 unsigned RunControlContext::threadId() const |
|
164 { |
|
165 return threadIdFromTcdfId(id); |
|
166 } |
|
167 |
|
168 unsigned RunControlContext::processIdFromTcdfId(const QByteArray &id) |
|
169 { |
|
170 // Cut out process id from "p12" or "p12.t34"? |
|
171 if (!id.startsWith('p')) |
|
172 return 0; |
|
173 const int dotPos = id.indexOf('.'); |
|
174 const int pLen = dotPos == -1 ? id.size() : dotPos; |
|
175 return id.mid(1, pLen - 1).toUInt(); |
|
176 } |
|
177 |
|
178 unsigned RunControlContext::threadIdFromTcdfId(const QByteArray &id) |
|
179 { |
|
180 const int tPos = id.indexOf(".t"); |
|
181 return tPos != -1 ? id.mid(tPos + 2).toUInt() : uint(0); |
|
182 } |
|
183 |
|
184 QByteArray RunControlContext::tcfId(unsigned processId, unsigned threadId /* = 0 */) |
|
185 { |
|
186 QByteArray rc("p"); |
|
187 rc += QByteArray::number(processId); |
|
188 if (threadId) { |
|
189 rc += ".t"; |
|
190 rc += QByteArray::number(threadId); |
|
191 } |
|
192 return rc; |
|
193 } |
|
194 |
|
195 RunControlContext::Type RunControlContext::type() const |
|
196 { |
|
197 return RunControlContext::typeFromTcfId(id); |
|
198 } |
|
199 |
|
200 bool RunControlContext::parse(const JsonValue &val) |
|
201 { |
|
202 clear(); |
|
203 if (val.type() != JsonValue::Object) |
|
204 return false; |
|
205 foreach(const JsonValue &c, val.children()) { |
|
206 if (c.name() == "ID") { |
|
207 id = c.data(); |
|
208 } else if (c.name() == "OSID") { |
|
209 osid = c.data(); |
|
210 } else if (c.name() == "ParentID") { |
|
211 parentId = c.data(); |
|
212 } else if (c.name() == "IsContainer") { |
|
213 if (jsonToBool(c)) |
|
214 flags |= Container; |
|
215 } else if (c.name() == "CanTerminate") { |
|
216 if (jsonToBool(c)) |
|
217 flags |= CanTerminate; |
|
218 } else if (c.name() == "CanResume") { |
|
219 resumeFlags = c.data().toUInt(); |
|
220 } else if (c.name() == "HasState") { |
|
221 if (jsonToBool(c)) |
|
222 flags |= HasState; |
|
223 } else if (c.name() == "CanSuspend") { |
|
224 if (jsonToBool(c)) |
|
225 flags |= CanSuspend; |
|
226 } |
|
227 } |
|
228 return true; |
|
229 } |
|
230 |
|
231 QString RunControlContext::toString() const |
|
232 { |
|
233 QString rc; |
|
234 QTextStream str(&rc); |
|
235 format(str); |
|
236 return rc; |
|
237 } |
|
238 |
|
239 void RunControlContext::format(QTextStream &str) const |
|
240 { |
|
241 str << " id='" << id << "' osid='" << osid |
|
242 << "' parentId='" << parentId <<"' "; |
|
243 if (flags & Container) |
|
244 str << "[container] "; |
|
245 if (flags & HasState) |
|
246 str << "[has state] "; |
|
247 if (flags & CanSuspend) |
|
248 str << "[can suspend] "; |
|
249 if (flags & CanSuspend) |
|
250 str << "[can terminate] "; |
|
251 str.setIntegerBase(16); |
|
252 str << " resume_flags: 0x" << resumeFlags; |
|
253 str.setIntegerBase(10); |
|
254 } |
|
255 |
|
256 // ------ ModuleLoadEventInfo |
|
257 ModuleLoadEventInfo::ModuleLoadEventInfo() : |
|
258 loaded(false), codeAddress(0), dataAddress(0), requireResume(false) |
|
259 { |
|
260 } |
|
261 |
|
262 void ModuleLoadEventInfo::clear() |
|
263 { |
|
264 loaded = requireResume = false; |
|
265 codeAddress = dataAddress =0; |
|
266 } |
|
267 |
|
268 bool ModuleLoadEventInfo::parse(const JsonValue &val) |
|
269 { |
|
270 clear(); |
|
271 if (val.type() != JsonValue::Object) |
|
272 return false; |
|
273 foreach(const JsonValue &c, val.children()) { |
|
274 if (c.name() == "Name") { |
|
275 name = c.data(); |
|
276 } else if (c.name() == "File") { |
|
277 file = c.data(); |
|
278 } else if (c.name() == "CodeAddress") { |
|
279 codeAddress = c.data().toULongLong(); |
|
280 } else if (c.name() == "DataAddress") { |
|
281 dataAddress = c.data().toULongLong(); |
|
282 } else if (c.name() == "Loaded") { |
|
283 loaded = jsonToBool(c); |
|
284 } else if (c.name() == "RequireResume") { |
|
285 requireResume =jsonToBool(c); |
|
286 } |
|
287 } |
|
288 return true; |
|
289 } |
|
290 void ModuleLoadEventInfo::format(QTextStream &str) const |
|
291 { |
|
292 str << "name='" << name << "' file='" << file << "' " << |
|
293 (loaded ? "[loaded] " : "[not loaded] "); |
|
294 if (requireResume) |
|
295 str << "[requires resume] "; |
|
296 str.setIntegerBase(16); |
|
297 str << " code: 0x" << codeAddress << " data: 0x" << dataAddress; |
|
298 str.setIntegerBase(10); |
|
299 } |
|
300 |
|
301 // ---------------------- Breakpoint |
|
302 |
|
303 // Types matching enum |
|
304 static const char *breakPointTypesC[] = {"Software", "Hardware", "Auto"}; |
|
305 |
|
306 Breakpoint::Breakpoint(quint64 loc) : |
|
307 type(Auto), enabled(true), ignoreCount(0), location(loc), size(1), thumb(true) |
|
308 { |
|
309 if (loc) |
|
310 id = idFromLocation(location); |
|
311 } |
|
312 |
|
313 void Breakpoint::setContextId(unsigned processId, unsigned threadId) |
|
314 { |
|
315 contextIds = QVector<QByteArray>(1, RunControlContext::tcfId(processId, threadId)); |
|
316 } |
|
317 |
|
318 QByteArray Breakpoint::idFromLocation(quint64 loc) |
|
319 { |
|
320 return QByteArray("BP_0x") + QByteArray::number(loc, 16); |
|
321 } |
|
322 |
|
323 QString Breakpoint::toString() const |
|
324 { |
|
325 QString rc; |
|
326 QTextStream str(&rc); |
|
327 str.setIntegerBase(16); |
|
328 str << "Breakpoint '" << id << "' " << breakPointTypesC[type] << " for contexts '" |
|
329 << joinByteArrays(contextIds, ',') << "' at 0x" << location; |
|
330 str.setIntegerBase(10); |
|
331 str << " size " << size; |
|
332 if (enabled) |
|
333 str << " [enabled]"; |
|
334 if (thumb) |
|
335 str << " [thumb]"; |
|
336 if (ignoreCount) |
|
337 str << " IgnoreCount " << ignoreCount; |
|
338 return rc; |
|
339 } |
|
340 |
|
341 JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b) |
|
342 { |
|
343 if (b.contextIds.isEmpty()) |
|
344 qWarning("tcftrk::Breakpoint: No context ids specified"); |
|
345 |
|
346 str << '{' << "ID" << ':' << QString::fromUtf8(b.id) << ',' |
|
347 << "BreakpointType" << ':' << breakPointTypesC[b.type] << ',' |
|
348 << "Enabled" << ':' << b.enabled << ',' |
|
349 << "IgnoreCount" << ':' << b.ignoreCount << ',' |
|
350 << "ContextIds" << ':' << b.contextIds << ',' |
|
351 << "Location" << ':' << QString::number(b.location) << ',' |
|
352 << "Size" << ':' << b.size << ',' |
|
353 << "THUMB_BREAKPOINT" << ':' << b.thumb |
|
354 << '}'; |
|
355 return str; |
|
356 } |
|
357 |
|
358 // --- Events |
|
359 TcfTrkEvent::TcfTrkEvent(Type type) : m_type(type) |
|
360 { |
|
361 } |
|
362 |
|
363 TcfTrkEvent::~TcfTrkEvent() |
|
364 { |
|
365 } |
|
366 |
|
367 TcfTrkEvent::Type TcfTrkEvent::type() const |
|
368 { |
|
369 return m_type; |
|
370 } |
|
371 |
|
372 QString TcfTrkEvent::toString() const |
|
373 { |
|
374 return QString(); |
|
375 } |
|
376 |
|
377 static const char sharedLibrarySuspendReasonC[] = "Shared Library"; |
|
378 |
|
379 TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const QVector<JsonValue> &values) |
|
380 { |
|
381 switch (s) { |
|
382 case LocatorService: |
|
383 if (nameBA == "Hello" && values.size() == 1 && values.front().type() == JsonValue::Array) { |
|
384 QStringList services; |
|
385 foreach (const JsonValue &jv, values.front().children()) |
|
386 services.push_back(QString::fromUtf8(jv.data())); |
|
387 return new TcfTrkLocatorHelloEvent(services); |
|
388 } |
|
389 break; |
|
390 case RunControlService: |
|
391 if (values.empty()) |
|
392 return 0; |
|
393 // "id/PC/Reason/Data" |
|
394 if (nameBA == "contextSuspended" && values.size() == 4) { |
|
395 const QByteArray idBA = values.at(0).data(); |
|
396 const quint64 pc = values.at(1).data().toULongLong(); |
|
397 const QByteArray reasonBA = values.at(2).data(); |
|
398 // Module load: Special |
|
399 if (reasonBA == sharedLibrarySuspendReasonC) { |
|
400 ModuleLoadEventInfo info; |
|
401 if (!info.parse(values.at(3))) |
|
402 return 0; |
|
403 return new TcfTrkRunControlModuleLoadContextSuspendedEvent(idBA, reasonBA, pc, info); |
|
404 } |
|
405 return new TcfTrkRunControlContextSuspendedEvent(idBA, reasonBA, pc); |
|
406 } // "contextSuspended" |
|
407 if (nameBA == "contextAdded") |
|
408 return TcfTrkRunControlContextAddedEvent::parseEvent(values); |
|
409 if (nameBA == "contextRemoved" && values.front().type() == JsonValue::Array) { |
|
410 QVector<QByteArray> ids; |
|
411 foreach(const JsonValue &c, values.front().children()) |
|
412 ids.push_back(c.data()); |
|
413 return new TcfTrkRunControlContextRemovedEvent(ids); |
|
414 } |
|
415 break; |
|
416 default: |
|
417 break; |
|
418 } |
|
419 return 0; |
|
420 } |
|
421 |
|
422 // -------------- TcfTrkServiceHelloEvent |
|
423 TcfTrkLocatorHelloEvent::TcfTrkLocatorHelloEvent(const QStringList &s) : |
|
424 TcfTrkEvent(LocatorHello), |
|
425 m_services(s) |
|
426 { |
|
427 } |
|
428 |
|
429 QString TcfTrkLocatorHelloEvent::toString() const |
|
430 { |
|
431 return QLatin1String("ServiceHello: ") + m_services.join(QLatin1String(", ")); |
|
432 } |
|
433 |
|
434 // -------------- TcfTrkIdEvent |
|
435 TcfTrkIdEvent::TcfTrkIdEvent(Type t, const QByteArray &id) : |
|
436 TcfTrkEvent(t), m_id(id) |
|
437 { |
|
438 } |
|
439 |
|
440 // ---------- TcfTrkIdsEvent |
|
441 TcfTrkIdsEvent::TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids) : |
|
442 TcfTrkEvent(t), m_ids(ids) |
|
443 { |
|
444 } |
|
445 |
|
446 QString TcfTrkIdsEvent::joinedIdString(const char sep) const |
|
447 { |
|
448 return joinByteArrays(m_ids, sep); |
|
449 } |
|
450 |
|
451 // ---------------- TcfTrkRunControlContextAddedEvent |
|
452 TcfTrkRunControlContextAddedEvent::TcfTrkRunControlContextAddedEvent(const RunControlContexts &c) : |
|
453 TcfTrkEvent(RunControlContextAdded), m_contexts(c) |
|
454 { |
|
455 } |
|
456 |
|
457 TcfTrkRunControlContextAddedEvent |
|
458 *TcfTrkRunControlContextAddedEvent::parseEvent(const QVector<JsonValue> &values) |
|
459 { |
|
460 // Parse array of contexts |
|
461 if (values.size() < 1 || values.front().type() != JsonValue::Array) |
|
462 return 0; |
|
463 |
|
464 RunControlContexts contexts; |
|
465 foreach (const JsonValue &v, values.front().children()) { |
|
466 RunControlContext context; |
|
467 if (context.parse(v)) |
|
468 contexts.push_back(context); |
|
469 } |
|
470 return new TcfTrkRunControlContextAddedEvent(contexts); |
|
471 } |
|
472 |
|
473 QString TcfTrkRunControlContextAddedEvent::toString() const |
|
474 { |
|
475 QString rc; |
|
476 QTextStream str(&rc); |
|
477 str << "RunControl: " << m_contexts.size() << " context(s) " |
|
478 << (type() == RunControlContextAdded ? "added" : "removed") |
|
479 << '\n'; |
|
480 foreach (const RunControlContext &c, m_contexts) { |
|
481 c.format(str); |
|
482 str << '\n'; |
|
483 } |
|
484 return rc; |
|
485 } |
|
486 |
|
487 // --------------- TcfTrkRunControlContextRemovedEvent |
|
488 TcfTrkRunControlContextRemovedEvent::TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &ids) : |
|
489 TcfTrkIdsEvent(RunControlContextRemoved, ids) |
|
490 { |
|
491 } |
|
492 |
|
493 QString TcfTrkRunControlContextRemovedEvent::toString() const |
|
494 { |
|
495 return QLatin1String("RunControl: Removed contexts '") + joinedIdString() + ("'."); |
|
496 } |
|
497 |
|
498 // --------------- TcfTrkRunControlContextSuspendedEvent |
|
499 TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(const QByteArray &id, |
|
500 const QByteArray &reason, |
|
501 quint64 pc) : |
|
502 TcfTrkIdEvent(RunControlSuspended, id), m_pc(pc), m_reason(reason) |
|
503 { |
|
504 } |
|
505 |
|
506 TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(Type t, |
|
507 const QByteArray &id, |
|
508 const QByteArray &reason, |
|
509 quint64 pc) : |
|
510 TcfTrkIdEvent(t, id), m_pc(pc), m_reason(reason) |
|
511 { |
|
512 } |
|
513 |
|
514 void TcfTrkRunControlContextSuspendedEvent::format(QTextStream &str) const |
|
515 { |
|
516 str.setIntegerBase(16); |
|
517 str << "RunControl: '" << idString() << "' suspended at 0x" |
|
518 << m_pc << ": '" << m_reason << "'."; |
|
519 str.setIntegerBase(10); |
|
520 } |
|
521 |
|
522 QString TcfTrkRunControlContextSuspendedEvent::toString() const |
|
523 { |
|
524 QString rc; |
|
525 QTextStream str(&rc); |
|
526 format(str); |
|
527 return rc; |
|
528 } |
|
529 |
|
530 TcfTrkRunControlContextSuspendedEvent::Reason TcfTrkRunControlContextSuspendedEvent::reason() const |
|
531 { |
|
532 if (m_reason == sharedLibrarySuspendReasonC) |
|
533 return ModuleLoad; |
|
534 if (m_reason == "Breakpoint") |
|
535 return BreakPoint; |
|
536 // 'Data abort exception'/'Thread has panicked' ... unfortunately somewhat unspecific. |
|
537 if (m_reason.contains("exception") || m_reason.contains("panick")) |
|
538 return Crash; |
|
539 return Other; |
|
540 } |
|
541 |
|
542 TcfTrkRunControlModuleLoadContextSuspendedEvent::TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id, |
|
543 const QByteArray &reason, |
|
544 quint64 pc, |
|
545 const ModuleLoadEventInfo &mi) : |
|
546 TcfTrkRunControlContextSuspendedEvent(RunControlModuleLoadSuspended, id, reason, pc), |
|
547 m_mi(mi) |
|
548 { |
|
549 } |
|
550 |
|
551 QString TcfTrkRunControlModuleLoadContextSuspendedEvent::toString() const |
|
552 { |
|
553 QString rc; |
|
554 QTextStream str(&rc); |
|
555 TcfTrkRunControlContextSuspendedEvent::format(str); |
|
556 str << ' '; |
|
557 m_mi.format(str); |
|
558 return rc; |
|
559 } |
|
560 |
|
561 |
|
562 } // namespace tcftrk |