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