1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifdef JS_CACHEIR_SPEW
8
9 # include "jit/CacheIRSpewer.h"
10
11 # include "mozilla/Sprintf.h"
12
13 # include <algorithm>
14 # include <stdarg.h>
15
16 # include "jsmath.h"
17
18 # include "js/ScalarType.h" // js::Scalar::Type
19 # include "util/GetPidProvider.h"
20 # include "util/Text.h"
21 # include "vm/JSFunction.h"
22 # include "vm/JSObject.h"
23 # include "vm/JSScript.h"
24
25 # include "vm/JSObject-inl.h"
26 # include "vm/Realm-inl.h"
27
28 using namespace js;
29 using namespace js::jit;
30
31 // Text spewer for CacheIR ops that can be used with JitSpew.
32 // Output looks like this:
33 //
34 // GuardToInt32 inputId 0, resultId 2
35 // GuardToInt32 inputId 1, resultId 3
36 // CompareInt32Result op JSOp::Lt, lhsId 2, rhsId 3
37 // ReturnFromIC
38 class MOZ_RAII CacheIROpsJitSpewer {
39 GenericPrinter& out_;
40
41 // String prepended to each line. Can be used for indentation.
42 const char* prefix_;
43
44 CACHE_IR_SPEWER_GENERATED
45
spewOp(CacheOp op)46 void spewOp(CacheOp op) {
47 const char* opName = CacheIROpNames[size_t(op)];
48 out_.printf("%s%-30s", prefix_, opName);
49 }
spewOpEnd()50 void spewOpEnd() { out_.printf("\n"); }
51
spewArgSeparator()52 void spewArgSeparator() { out_.printf(", "); }
53
spewOperandId(const char * name,OperandId id)54 void spewOperandId(const char* name, OperandId id) {
55 spewRawOperandId(name, id.id());
56 }
spewRawOperandId(const char * name,uint32_t id)57 void spewRawOperandId(const char* name, uint32_t id) {
58 out_.printf("%s %u", name, id);
59 }
spewField(const char * name,uint32_t offset)60 void spewField(const char* name, uint32_t offset) {
61 out_.printf("%s %u", name, offset);
62 }
spewBoolImm(const char * name,bool b)63 void spewBoolImm(const char* name, bool b) {
64 out_.printf("%s %s", name, b ? "true" : "false");
65 }
spewByteImm(const char * name,uint8_t val)66 void spewByteImm(const char* name, uint8_t val) {
67 out_.printf("%s %u", name, val);
68 }
spewJSOpImm(const char * name,JSOp op)69 void spewJSOpImm(const char* name, JSOp op) {
70 out_.printf("%s JSOp::%s", name, CodeName(op));
71 }
spewStaticStringImm(const char * name,const char * str)72 void spewStaticStringImm(const char* name, const char* str) {
73 out_.printf("%s \"%s\"", name, str);
74 }
spewInt32Imm(const char * name,int32_t val)75 void spewInt32Imm(const char* name, int32_t val) {
76 out_.printf("%s %d", name, val);
77 }
spewUInt32Imm(const char * name,uint32_t val)78 void spewUInt32Imm(const char* name, uint32_t val) {
79 out_.printf("%s %u", name, val);
80 }
spewCallFlagsImm(const char * name,CallFlags flags)81 void spewCallFlagsImm(const char* name, CallFlags flags) {
82 out_.printf(
83 "%s (format %u%s%s%s)", name, flags.getArgFormat(),
84 flags.isConstructing() ? ", isConstructing" : "",
85 flags.isSameRealm() ? ", isSameRealm" : "",
86 flags.needsUninitializedThis() ? ", needsUninitializedThis" : "");
87 }
spewJSWhyMagicImm(const char * name,JSWhyMagic magic)88 void spewJSWhyMagicImm(const char* name, JSWhyMagic magic) {
89 out_.printf("%s JSWhyMagic(%u)", name, unsigned(magic));
90 }
spewScalarTypeImm(const char * name,Scalar::Type type)91 void spewScalarTypeImm(const char* name, Scalar::Type type) {
92 out_.printf("%s Scalar::Type(%u)", name, unsigned(type));
93 }
spewUnaryMathFunctionImm(const char * name,UnaryMathFunction fun)94 void spewUnaryMathFunctionImm(const char* name, UnaryMathFunction fun) {
95 const char* funName = GetUnaryMathFunctionName(fun);
96 out_.printf("%s UnaryMathFunction::%s", name, funName);
97 }
spewValueTypeImm(const char * name,ValueType type)98 void spewValueTypeImm(const char* name, ValueType type) {
99 out_.printf("%s ValueType(%u)", name, unsigned(type));
100 }
spewJSNativeImm(const char * name,JSNative native)101 void spewJSNativeImm(const char* name, JSNative native) {
102 out_.printf("%s %p", name, native);
103 }
spewGuardClassKindImm(const char * name,GuardClassKind kind)104 void spewGuardClassKindImm(const char* name, GuardClassKind kind) {
105 out_.printf("%s GuardClassKind(%u)", name, unsigned(kind));
106 }
spewWasmValTypeImm(const char * name,wasm::ValType::Kind kind)107 void spewWasmValTypeImm(const char* name, wasm::ValType::Kind kind) {
108 out_.printf("%s WasmValTypeKind(%u)", name, unsigned(kind));
109 }
spewAllocKindImm(const char * name,gc::AllocKind kind)110 void spewAllocKindImm(const char* name, gc::AllocKind kind) {
111 out_.printf("%s AllocKind(%u)", name, unsigned(kind));
112 }
113
114 public:
CacheIROpsJitSpewer(GenericPrinter & out,const char * prefix)115 CacheIROpsJitSpewer(GenericPrinter& out, const char* prefix)
116 : out_(out), prefix_(prefix) {}
117
spew(CacheIRReader & reader)118 void spew(CacheIRReader& reader) {
119 do {
120 switch (reader.readOp()) {
121 # define SPEW_OP(op, ...) \
122 case CacheOp::op: \
123 spew##op(reader); \
124 break;
125 CACHE_IR_OPS(SPEW_OP)
126 # undef SPEW_OP
127
128 default:
129 MOZ_CRASH("Invalid op");
130 }
131 } while (reader.more());
132 }
133 };
134
SpewCacheIROps(GenericPrinter & out,const char * prefix,const CacheIRStubInfo * info)135 void js::jit::SpewCacheIROps(GenericPrinter& out, const char* prefix,
136 const CacheIRStubInfo* info) {
137 CacheIRReader reader(info);
138 CacheIROpsJitSpewer spewer(out, prefix);
139 spewer.spew(reader);
140 }
141
142 // JSON spewer for CacheIR ops. Output looks like this:
143 //
144 // ...
145 // {
146 // "op":"GuardToInt32",
147 // "args":[
148 // {
149 // "name":"inputId",
150 // "type":"Id",
151 // "value":0
152 // },
153 // {
154 // "name":"resultId",
155 // "type":"Id",
156 // "value":1
157 // }
158 // ]
159 // },
160 // {
161 // "op":"Int32IncResult",
162 // "args":[
163 // {
164 // "name":"inputId",
165 // "type":"Id",
166 // "value":1
167 // }
168 // ]
169 // }
170 // ...
171 class MOZ_RAII CacheIROpsJSONSpewer {
172 JSONPrinter& j_;
173
174 CACHE_IR_SPEWER_GENERATED
175
spewOp(CacheOp op)176 void spewOp(CacheOp op) {
177 const char* opName = CacheIROpNames[size_t(op)];
178 j_.beginObject();
179 j_.property("op", opName);
180 j_.beginListProperty("args");
181 }
spewOpEnd()182 void spewOpEnd() {
183 j_.endList();
184 j_.endObject();
185 }
186
spewArgSeparator()187 void spewArgSeparator() {}
188
189 template <typename T>
spewArgImpl(const char * name,const char * type,T value)190 void spewArgImpl(const char* name, const char* type, T value) {
191 j_.beginObject();
192 j_.property("name", name);
193 j_.property("type", type);
194 j_.property("value", value);
195 j_.endObject();
196 }
197
spewOperandId(const char * name,OperandId id)198 void spewOperandId(const char* name, OperandId id) {
199 spewRawOperandId(name, id.id());
200 }
spewRawOperandId(const char * name,uint32_t id)201 void spewRawOperandId(const char* name, uint32_t id) {
202 spewArgImpl(name, "Id", id);
203 }
spewField(const char * name,uint32_t offset)204 void spewField(const char* name, uint32_t offset) {
205 spewArgImpl(name, "Field", offset);
206 }
spewBoolImm(const char * name,bool b)207 void spewBoolImm(const char* name, bool b) { spewArgImpl(name, "Imm", b); }
spewByteImm(const char * name,uint8_t val)208 void spewByteImm(const char* name, uint8_t val) {
209 spewArgImpl(name, "Imm", val);
210 }
spewJSOpImm(const char * name,JSOp op)211 void spewJSOpImm(const char* name, JSOp op) {
212 spewArgImpl(name, "JSOp", CodeName(op));
213 }
spewStaticStringImm(const char * name,const char * str)214 void spewStaticStringImm(const char* name, const char* str) {
215 spewArgImpl(name, "String", str);
216 }
spewInt32Imm(const char * name,int32_t val)217 void spewInt32Imm(const char* name, int32_t val) {
218 spewArgImpl(name, "Imm", val);
219 }
spewUInt32Imm(const char * name,uint32_t val)220 void spewUInt32Imm(const char* name, uint32_t val) {
221 spewArgImpl(name, "Imm", val);
222 }
spewCallFlagsImm(const char * name,CallFlags flags)223 void spewCallFlagsImm(const char* name, CallFlags flags) {
224 spewArgImpl(name, "Imm", flags.toByte());
225 }
spewJSWhyMagicImm(const char * name,JSWhyMagic magic)226 void spewJSWhyMagicImm(const char* name, JSWhyMagic magic) {
227 spewArgImpl(name, "Imm", unsigned(magic));
228 }
spewScalarTypeImm(const char * name,Scalar::Type type)229 void spewScalarTypeImm(const char* name, Scalar::Type type) {
230 spewArgImpl(name, "Imm", unsigned(type));
231 }
spewUnaryMathFunctionImm(const char * name,UnaryMathFunction fun)232 void spewUnaryMathFunctionImm(const char* name, UnaryMathFunction fun) {
233 const char* funName = GetUnaryMathFunctionName(fun);
234 spewArgImpl(name, "MathFunction", funName);
235 }
spewValueTypeImm(const char * name,ValueType type)236 void spewValueTypeImm(const char* name, ValueType type) {
237 spewArgImpl(name, "Imm", unsigned(type));
238 }
spewJSNativeImm(const char * name,JSNative native)239 void spewJSNativeImm(const char* name, JSNative native) {
240 spewArgImpl(name, "Word", uintptr_t(native));
241 }
spewGuardClassKindImm(const char * name,GuardClassKind kind)242 void spewGuardClassKindImm(const char* name, GuardClassKind kind) {
243 spewArgImpl(name, "Imm", unsigned(kind));
244 }
spewWasmValTypeImm(const char * name,wasm::ValType::Kind kind)245 void spewWasmValTypeImm(const char* name, wasm::ValType::Kind kind) {
246 spewArgImpl(name, "Imm", unsigned(kind));
247 }
spewAllocKindImm(const char * name,gc::AllocKind kind)248 void spewAllocKindImm(const char* name, gc::AllocKind kind) {
249 spewArgImpl(name, "Imm", unsigned(kind));
250 }
251
252 public:
CacheIROpsJSONSpewer(JSONPrinter & j)253 explicit CacheIROpsJSONSpewer(JSONPrinter& j) : j_(j) {}
254
spew(CacheIRReader & reader)255 void spew(CacheIRReader& reader) {
256 do {
257 switch (reader.readOp()) {
258 # define SPEW_OP(op, ...) \
259 case CacheOp::op: \
260 spew##op(reader); \
261 break;
262 CACHE_IR_OPS(SPEW_OP)
263 # undef SPEW_OP
264
265 default:
266 MOZ_CRASH("Invalid op");
267 }
268 } while (reader.more());
269 }
270 };
271
272 CacheIRSpewer CacheIRSpewer::cacheIRspewer;
273
CacheIRSpewer()274 CacheIRSpewer::CacheIRSpewer()
275 : outputLock_(mutexid::CacheIRSpewer), guardCount_(0) {
276 spewInterval_ =
277 getenv("CACHEIR_LOG_FLUSH") ? atoi(getenv("CACHEIR_LOG_FLUSH")) : 10000;
278
279 if (spewInterval_ < 1) {
280 spewInterval_ = 1;
281 }
282 }
283
~CacheIRSpewer()284 CacheIRSpewer::~CacheIRSpewer() {
285 if (!enabled()) {
286 return;
287 }
288
289 json_.ref().endList();
290 output_.flush();
291 output_.finish();
292 }
293
294 # ifndef JIT_SPEW_DIR
295 # if defined(_WIN32)
296 # define JIT_SPEW_DIR "."
297 # elif defined(__ANDROID__)
298 # define JIT_SPEW_DIR "/data/local/tmp"
299 # else
300 # define JIT_SPEW_DIR "/tmp"
301 # endif
302 # endif
303
init(const char * filename)304 bool CacheIRSpewer::init(const char* filename) {
305 if (enabled()) {
306 return true;
307 }
308
309 char name[256];
310 uint32_t pid = getpid();
311 // Default to JIT_SPEW_DIR/cacheir${pid}.json
312 if (filename[0] == '1') {
313 SprintfLiteral(name, JIT_SPEW_DIR "/cacheir%" PRIu32 ".json", pid);
314 } else {
315 SprintfLiteral(name, "%s%" PRIu32 ".json", filename, pid);
316 }
317
318 if (!output_.init(name)) {
319 return false;
320 }
321
322 json_.emplace(output_);
323 json_->beginList();
324 return true;
325 }
326
beginCache(const IRGenerator & gen)327 void CacheIRSpewer::beginCache(const IRGenerator& gen) {
328 MOZ_ASSERT(enabled());
329 JSONPrinter& j = json_.ref();
330 const char* filename = gen.script_->filename();
331 j.beginObject();
332 j.property("name", CacheKindNames[uint8_t(gen.cacheKind_)]);
333 j.property("file", filename ? filename : "null");
334 j.property("mode", int(gen.mode_));
335 if (jsbytecode* pc = gen.pc_) {
336 unsigned column;
337 j.property("line", PCToLineNumber(gen.script_, pc, &column));
338 j.property("column", column);
339 j.formatProperty("pc", "%p", pc);
340 }
341 }
342
343 template <typename CharT>
QuoteString(GenericPrinter & out,const CharT * s,size_t length)344 static void QuoteString(GenericPrinter& out, const CharT* s, size_t length) {
345 const CharT* end = s + length;
346 for (const CharT* t = s; t < end; s = ++t) {
347 // This quote implementation is probably correct,
348 // but uses \u even when not strictly necessary.
349 char16_t c = *t;
350 if (c == '"' || c == '\\') {
351 out.printf("\\");
352 out.printf("%c", char(c));
353 } else if (!IsAsciiPrintable(c)) {
354 out.printf("\\u%04x", c);
355 } else {
356 out.printf("%c", char(c));
357 }
358 }
359 }
360
QuoteString(GenericPrinter & out,JSLinearString * str)361 static void QuoteString(GenericPrinter& out, JSLinearString* str) {
362 JS::AutoCheckCannotGC nogc;
363
364 // Limit the string length to reduce the JSON file size.
365 size_t length = std::min(str->length(), size_t(128));
366 if (str->hasLatin1Chars()) {
367 QuoteString(out, str->latin1Chars(nogc), length);
368 } else {
369 QuoteString(out, str->twoByteChars(nogc), length);
370 }
371 }
372
valueProperty(const char * name,const Value & v)373 void CacheIRSpewer::valueProperty(const char* name, const Value& v) {
374 MOZ_ASSERT(enabled());
375 JSONPrinter& j = json_.ref();
376
377 j.beginObjectProperty(name);
378
379 const char* type = InformalValueTypeName(v);
380 if (v.isInt32()) {
381 type = "int32";
382 }
383 j.property("type", type);
384
385 if (v.isInt32()) {
386 j.property("value", v.toInt32());
387 } else if (v.isDouble()) {
388 j.floatProperty("value", v.toDouble(), 3);
389 } else if (v.isString() || v.isSymbol()) {
390 JSString* str = v.isString() ? v.toString() : v.toSymbol()->description();
391 if (str && str->isLinear()) {
392 j.beginStringProperty("value");
393 QuoteString(output_, &str->asLinear());
394 j.endStringProperty();
395 }
396 } else if (v.isObject()) {
397 JSObject& object = v.toObject();
398 j.formatProperty("value", "%p (shape: %p)", &object, object.shape());
399
400 if (object.is<JSFunction>()) {
401 if (JSAtom* name = object.as<JSFunction>().displayAtom()) {
402 j.beginStringProperty("funName");
403 QuoteString(output_, name);
404 j.endStringProperty();
405 }
406 }
407
408 if (NativeObject* nobj =
409 object.is<NativeObject>() ? &object.as<NativeObject>() : nullptr) {
410 j.beginListProperty("flags");
411 {
412 if (nobj->isIndexed()) {
413 j.value("indexed");
414 }
415 if (nobj->inDictionaryMode()) {
416 j.value("dictionaryMode");
417 }
418 }
419 j.endList();
420 if (nobj->isIndexed()) {
421 j.beginObjectProperty("indexed");
422 {
423 j.property("denseInitializedLength",
424 nobj->getDenseInitializedLength());
425 j.property("denseCapacity", nobj->getDenseCapacity());
426 j.property("denseElementsAreSealed", nobj->denseElementsAreSealed());
427 j.property("denseElementsAreFrozen", nobj->denseElementsAreFrozen());
428 }
429 j.endObject();
430 }
431 }
432 }
433
434 j.endObject();
435 }
436
opcodeProperty(const char * name,const JSOp op)437 void CacheIRSpewer::opcodeProperty(const char* name, const JSOp op) {
438 MOZ_ASSERT(enabled());
439 JSONPrinter& j = json_.ref();
440
441 j.beginStringProperty(name);
442 output_.put(CodeName(op));
443 j.endStringProperty();
444 }
445
cacheIRSequence(CacheIRReader & reader)446 void CacheIRSpewer::cacheIRSequence(CacheIRReader& reader) {
447 MOZ_ASSERT(enabled());
448 JSONPrinter& j = json_.ref();
449
450 j.beginListProperty("cacheIR");
451
452 CacheIROpsJSONSpewer spewer(j);
453 spewer.spew(reader);
454
455 j.endList();
456 }
457
attached(const char * name)458 void CacheIRSpewer::attached(const char* name) {
459 MOZ_ASSERT(enabled());
460 json_.ref().property("attached", name);
461 }
462
endCache()463 void CacheIRSpewer::endCache() {
464 MOZ_ASSERT(enabled());
465 json_.ref().endObject();
466 }
467
468 #endif /* JS_CACHEIR_SPEW */
469