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