OSDN Git Service

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