1 /**************************************************************************
2 *
3 * Copyright 2010 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 **************************************************************************/
25
26
27 #include "trace_dump_internal.hpp"
28
29 #include <limits>
30
31 #include <assert.h>
32 #include <string.h>
33
34 #include "highlight.hpp"
35 #include "guids.hpp"
36
37
38 namespace trace {
39
40
Dumper(std::ostream & _os,DumpFlags _flags)41 Dumper::Dumper(std::ostream &_os, DumpFlags _flags) :
42 os(_os),
43 dumpFlags(_flags),
44 highlighter(highlight::defaultHighlighter(!(dumpFlags & DUMP_FLAG_NO_COLOR))),
45 normal(highlighter.normal()),
46 bold(highlighter.bold()),
47 italic(highlighter.italic()),
48 strike(highlighter.strike()),
49 red(highlighter.color(highlight::RED)),
50 pointer(highlighter.color(highlight::GREEN)),
51 literal(highlighter.color(highlight::BLUE))
52 {
53 }
54
~Dumper()55 Dumper::~Dumper() {
56 }
57
visit(Null *)58 void Dumper::visit(Null *) {
59 os << literal << "NULL" << normal;
60 }
61
visit(Bool * node)62 void Dumper::visit(Bool *node) {
63 os << literal << (node->value ? "true" : "false") << normal;
64 }
65
visit(SInt * node)66 void Dumper::visit(SInt *node) {
67 os << literal << node->value << normal;
68 }
69
visit(UInt * node)70 void Dumper::visit(UInt *node) {
71 os << literal << node->value << normal;
72 }
73
visit(Float * node)74 void Dumper::visit(Float *node) {
75 std::streamsize oldPrecision = os.precision(std::numeric_limits<float>::digits10 + 1);
76 os << literal << node->value << normal;
77 os.precision(oldPrecision);
78 }
79
visit(Double * node)80 void Dumper::visit(Double *node) {
81 std::streamsize oldPrecision = os.precision(std::numeric_limits<double>::digits10 + 1);
82 os << literal << node->value << normal;
83 os.precision(oldPrecision);
84 }
85
86 template< typename C >
visitString(const C * value)87 void Dumper::visitString(const C *value) {
88 os << literal << "\"";
89 for (const C *it = value; *it; ++it) {
90 unsigned c = (unsigned) *it;
91 if (c == '\"')
92 os << "\\\"";
93 else if (c == '\\')
94 os << "\\\\";
95 else if (c >= 0x20 && c <= 0x7e)
96 os << (char)c;
97 else if (c == '\t') {
98 os << "\t";
99 } else if (c == '\r') {
100 // Ignore carriage-return
101 } else if (c == '\n') {
102 if (dumpFlags & DUMP_FLAG_NO_MULTILINE) {
103 os << "\\n";
104 } else {
105 // Reset formatting so that it looks correct with 'less -R'
106 os << normal << '\n' << literal;
107 }
108 } else {
109 // FIXME: handle wchar_t octals properly
110 unsigned octal0 = c & 0x7;
111 unsigned octal1 = (c >> 3) & 0x7;
112 unsigned octal2 = (c >> 3) & 0x7;
113 os << "\\";
114 if (octal2)
115 os << octal2;
116 if (octal1)
117 os << octal1;
118 os << octal0;
119 }
120 }
121 os << "\"" << normal;
122 }
123
visit(String * node)124 void Dumper::visit(String *node) {
125 visitString(node->value);
126 }
127
visit(WString * node)128 void Dumper::visit(WString *node) {
129 os << literal << "L";
130 visitString(node->value);
131 }
132
visit(Enum * node)133 void Dumper::visit(Enum *node) {
134 const EnumValue *it = node->lookup();
135 if (it) {
136 os << literal << it->name << normal;
137 return;
138 }
139 os << literal << node->value << normal;
140 }
141
visit(Bitmask * bitmask)142 void Dumper::visit(Bitmask *bitmask) {
143 unsigned long long value = bitmask->value;
144 const BitmaskSig *sig = bitmask->sig;
145 bool first = true;
146 for (const BitmaskFlag *it = sig->flags; it != sig->flags + sig->num_flags; ++it) {
147 if ((it->value && (value & it->value) == it->value) ||
148 (!it->value && value == 0)) {
149 if (!first) {
150 os << " | ";
151 }
152 os << literal << it->name << normal;
153 value &= ~it->value;
154 first = false;
155 }
156 if (value == 0) {
157 break;
158 }
159 }
160 if (value || first) {
161 if (!first) {
162 os << " | ";
163 }
164 os << literal << "0x" << std::hex << value << std::dec << normal;
165 }
166 }
167
168 const char *
visitMembers(Struct * s,const char * sep)169 Dumper::visitMembers(Struct *s, const char *sep) {
170 for (unsigned i = 0; i < s->members.size(); ++i) {
171 const char *memberName = s->sig->member_names[i];
172 Value *memberValue = s->members[i];
173
174 if (!memberName || !*memberName) {
175 // Anonymous structure
176 Struct *memberStruct = memberValue->toStruct();
177 assert(memberStruct);
178 if (memberStruct) {
179 sep = visitMembers(memberStruct, sep);
180 continue;
181 }
182 }
183
184 os << sep << italic << memberName << normal << " = ",
185 _visit(memberValue);
186 sep = ", ";
187 }
188 return sep;
189 }
190
visit(Struct * s)191 void Dumper::visit(Struct *s) {
192 // Replace GUIDs with their symbolic name
193 // TODO: Move this to parsing, so it can be shared everywhere
194 if (s->members.size() == 4 &&
195 strcmp(s->sig->name, "GUID") == 0) {
196 GUID guid;
197 guid.Data1 = s->members[0]->toUInt();
198 guid.Data2 = s->members[1]->toUInt();
199 guid.Data3 = s->members[2]->toUInt();
200 Array *data4 = s->members[3]->toArray();
201 assert(data4);
202 assert(data4->values.size() == 8);
203 for (int i = 0; i < sizeof guid.Data4; ++i) {
204 guid.Data4[i] = data4->values[i]->toUInt();
205 }
206 const char *name = getGuidName(guid);
207 os << literal << name << normal;
208 return;
209 }
210
211 os << "{";
212 visitMembers(s);
213 os << "}";
214 }
215
visit(Array * array)216 void Dumper::visit(Array *array) {
217 if (array->values.size() == 1) {
218 os << "&";
219 _visit(array->values[0]);
220 }
221 else {
222 const char *sep = "";
223 os << "{";
224 for (auto & value : array->values) {
225 os << sep;
226 _visit(value);
227 sep = ", ";
228 }
229 os << "}";
230 }
231 }
232
visit(Blob * blob)233 void Dumper::visit(Blob *blob) {
234 os << pointer << "blob(" << blob->size << ")" << normal;
235 }
236
visit(Pointer * p)237 void Dumper::visit(Pointer *p) {
238 os << pointer << "0x" << std::hex << p->value << std::dec << normal;
239 }
240
visit(Repr * r)241 void Dumper::visit(Repr *r) {
242 _visit(r->humanValue);
243 }
244
visit(StackFrame * frame)245 void Dumper::visit(StackFrame *frame) {
246 frame->dump(os);
247 }
248
visit(Backtrace & backtrace)249 void Dumper::visit(Backtrace & backtrace) {
250 for (auto & frame : backtrace) {
251 visit(frame);
252 os << '\n';
253 }
254 }
255
visit(Call * call)256 void Dumper::visit(Call *call)
257 {
258 CallFlags callFlags = call->flags;
259
260 if (!(dumpFlags & DUMP_FLAG_NO_CALL_NO)) {
261 os << call->no << " ";
262 }
263 if (dumpFlags & DUMP_FLAG_THREAD_IDS) {
264 os << "@" << std::hex << call->thread_id << std::dec << " ";
265 }
266
267 if (callFlags & CALL_FLAG_NON_REPRODUCIBLE) {
268 os << strike;
269 } else if (callFlags & CALL_FLAG_NO_SIDE_EFFECTS) {
270 os << normal;
271 } else {
272 os << bold;
273 }
274 os << call->sig->name << normal;
275
276 os << "(";
277 const char *sep = "";
278 for (unsigned i = 0; i < call->args.size(); ++i) {
279 os << sep;
280 if (!(dumpFlags & DUMP_FLAG_NO_ARG_NAMES)) {
281 os << italic << call->sig->arg_names[i] << normal << " = ";
282 }
283 if (call->args[i].value) {
284 _visit(call->args[i].value);
285 } else {
286 os << "?";
287 }
288 sep = ", ";
289 }
290 os << ")";
291
292 if (call->ret) {
293 os << " = ";
294 _visit(call->ret);
295 }
296
297 if (callFlags & (CALL_FLAG_FAKE |
298 CALL_FLAG_INCOMPLETE)) {
299 os << " //";
300 if (callFlags & CALL_FLAG_FAKE) {
301 os << " " << "fake";
302 }
303 if (callFlags & CALL_FLAG_INCOMPLETE) {
304 os << " " << red << "incomplete" << normal;
305 }
306 }
307
308 if (!(dumpFlags & DUMP_FLAG_NO_MULTILINE)) {
309 os << '\n';
310
311 if (call->backtrace != NULL) {
312 os << bold << red << "Backtrace:\n" << normal;
313 visit(*call->backtrace);
314 }
315 if (callFlags & CALL_FLAG_END_FRAME) {
316 os << '\n';
317 }
318 }
319 }
320
321
dump(Value * value,std::ostream & os,DumpFlags flags)322 void dump(Value *value, std::ostream &os, DumpFlags flags) {
323 Dumper d(os, flags);
324 value->visit(d);
325 }
326
327
dump(Call & call,std::ostream & os,DumpFlags flags)328 void dump(Call &call, std::ostream &os, DumpFlags flags) {
329 Dumper d(os, flags);
330 d.visit(&call);
331 }
332
333
334 } /* namespace trace */
335