3 +----------------------------------------------------------------------+
5 +----------------------------------------------------------------------+
6 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
24 #include <folly/Format.h>
25 #include <folly/functional/Invoke.h>
26 #include <folly/portability/Unistd.h>
28 #include "hphp/util/assertions.h"
29 #include "hphp/util/compact-vector.h"
30 #include "hphp/util/portability.h"
31 #include "hphp/util/text-color.h"
34 * Runtime-selectable trace facility. A trace statement has both a module and a
35 * level associated with it; Enable tracing a module by setting the TRACE
36 * environment variable to a comma-separated list of module:level pairs. E.g.:
38 * env TRACE=mcg:1,bcinterp:3,tmp0:1 ./hhvm/hhvm ...
40 * In a source file, select the compilation unit's module by calling the
41 * TRACE_SET_MOD macro. E.g.,
46 * TRACE(0, "See this for any trace-enabled build: %d\n", foo);
47 * TRACE(1, "Trace-level must be 1 or higher for this one\n");
49 * While the levels are arbitrary integers, code so far is following a
50 * rough convention of 1-5, where 1 is minimal and 5 is quite verbose.
52 * Normally trace information is printed to /tmp/hphp.log. You can
53 * override the environment variable HPHP_TRACE_FILE to change
54 * this. (Note you can set it to something like /dev/stderr or
55 * /dev/stdout if you want the logs printed to your terminal).
57 * When printing to the terminal, some traces will know how to use
58 * colorization. You can set HPHP_TRACE_TTY to tell the tracing
59 * facility to assume it should colorize even if the output file isn't
64 * Trace levels can be bumped on a per-module, per-scope basis. This
65 * lets you run code that has a set of trace levels in a mode as if
66 * they were all higher.
71 * Trace::Bump bumper{Trace::mcg, 2};
72 * FTRACE(1, "asd\n"); // only fires at level >= 3
74 * FTRACE(1, "asd\n"); // back to normal
77 * There is also support for conditionally bumping in the bumper:
80 * Trace::Bump bumper{Trace::mcg, 2, somePredicate(foo)};
81 * // Only bumped if somePredicate(foo) returned true.
84 * Note however that if you use that form, `somePredicate' will be
85 * evaluated even if tracing is off.
91 #define TRACE_MODULES \
92 TM(tprefix) /* Meta: prefix with string */ \
93 TM(traceAsync) /* Meta: lazy writes to disk */ \
121 TM(hackc_translate) \
130 TM(hhbbc_dump_trace) \
143 TM(hhir_checkhoist) \
151 TM(hhir_lowerbespokes) \
156 TM(hhir_refineTmps) \
157 TM(hhir_checkTypes) \
159 TM(hhir_unreachable) \
215 TM(vasm_block_count) \
217 TM(vasm_graph_color) \
227 /* Stress categories, to exercise rare paths */ \
228 TM(stress_txInterpPct) \
229 TM(stress_txInterpSeed) \
230 /* Jit bisection interval */ \
233 /* Temporary categories, to save compilation time */ \
234 TM(tmp0) TM(tmp1) TM(tmp2) TM(tmp3) \
235 TM(tmp4) TM(tmp5) TM(tmp6) TM(tmp7) \
236 TM(tmp8) TM(tmp9) TM(tmp10) TM(tmp11) \
237 TM(tmp12) TM(tmp13) TM(tmp14) TM(tmp15)
247 //////////////////////////////////////////////////////////////////////
250 * S-expression style structured pretty-printing. Implement
251 * std::string pretty() const { }, with the convention that
252 * nested structures are notated as lisp-style trees:
254 * (<typename> field0 field1)
259 * The repetitve prettyNode() templates are intended to aid
260 * implementing pretty().
263 template<typename P1
>
264 std::string
prettyNode(const char* name
, const std::vector
<P1
>& vec
) {
266 std::string retval
= string("(") + string(name
) + string(" ");
267 for(size_t i
= 0; i
< vec
.size(); i
++) {
268 retval
+= vec
[i
].pretty();
269 if (i
!= vec
.size() - 1) {
270 retval
+= string(" ");
273 return retval
+ string(")");
276 template<typename P1
>
277 std::string
prettyNode(const char* name
, const P1
& p1
) {
279 return string("(") + string(name
) + string(" ") +
284 template<> std::string
prettyNode(const char* name
, const std::string
& s
);
286 template<typename P1
, typename P2
>
287 std::string
prettyNode(const char* name
, const P1
& p1
, const P2
& p2
) {
289 return string("(") + string(name
) + string(" ") +
290 p1
.pretty() + string(" ") + p2
.pretty() +
294 void traceRelease(ATTRIBUTE_PRINTF_STRING
const char*, ...)
295 ATTRIBUTE_PRINTF(1,2);
296 void traceRelease(const std::string
& s
);
298 template<typename
... Args
>
299 void ftraceRelease(Args
&&... args
) {
300 traceRelease("%s", folly::format(std::forward
<Args
>(args
)...).str().c_str());
303 // Trace to the global ring buffer and the normal TRACE destination.
304 #define TRACE_RB(n, ...) \
305 ONTRACE(n, HPHP::Trace::traceRingBufferRelease(__VA_ARGS__)); \
306 TRACE(n, __VA_ARGS__);
307 void traceRingBufferRelease(ATTRIBUTE_PRINTF_STRING
const char* fmt
, ...)
308 ATTRIBUTE_PRINTF(1,2);
310 extern int levels
[NumModules
];
311 extern __thread
int tl_levels
[NumModules
];
312 const char* moduleName(Module mod
);
313 inline bool moduleEnabledRelease(Module tm
, int level
= 1) {
314 return levels
[tm
] + tl_levels
[tm
] >= level
;
317 // Trace::Bump that is on for release tracing.
319 BumpRelease(Module mod
, int adjust
, bool condition
= true)
324 if (m_live
) tl_levels
[m_mod
] -= m_adjust
;
327 BumpRelease(BumpRelease
&& o
) noexcept
330 , m_adjust(o
.m_adjust
)
336 if (m_live
) tl_levels
[m_mod
] += m_adjust
;
339 BumpRelease
negate() const {
340 return BumpRelease
{ m_mod
, -m_adjust
, m_live
};
343 BumpRelease(const BumpRelease
&) = delete;
344 BumpRelease
& operator=(const BumpRelease
&) = delete;
352 CompactVector
<BumpRelease
> bumpSpec(folly::StringPiece traceSpec
);
354 //////////////////////////////////////////////////////////////////////
356 #if (!defined(NDEBUG) || defined(USE_TRACE)) /* { */
361 //////////////////////////////////////////////////////////////////////
363 * Implementation of for when tracing is enabled.
366 inline bool moduleEnabled(Module tm
, int level
= 1) {
367 return moduleEnabledRelease(tm
, level
);
370 inline int moduleLevel(Module tm
) { return levels
[tm
]; }
374 const bool enabled
= true;
376 #define ONTRACE_MOD(module, n, x) do { \
377 if (HPHP::Trace::moduleEnabled(module, n)) { \
381 #define ONTRACE(n, x) ONTRACE_MOD(TRACEMOD, n, x)
383 #define TRACE(n, ...) ONTRACE(n, HPHP::Trace::trace(__VA_ARGS__))
384 #define FTRACE(n, ...) \
385 ONTRACE(n, HPHP::Trace::trace("%s", \
386 folly::format(__VA_ARGS__).str().c_str()))
387 #define TRACE_MOD(mod, level, ...) \
388 ONTRACE_MOD(mod, level, HPHP::Trace::trace(__VA_ARGS__))
389 #define FTRACE_MOD(mod, level, ...) \
390 ONTRACE_MOD(mod, level, HPHP::Trace::trace("%s", \
391 folly::format(__VA_ARGS__).str().c_str()))
392 #define TRACE_SET_MOD(name) \
393 UNUSED static const HPHP::Trace::Module TRACEMOD = HPHP::Trace::name;
396 * The Indent struct and ITRACE are used for tracing with nested
397 * indentation. Create an Indent object on the stack to increase the nesting
398 * level, then use ITRACE just as you would use FTRACE.
400 extern __thread
int indentDepth
;
402 explicit Indent(int n
= 2) : n(n
) { indentDepth
+= n
; }
403 ~Indent() { indentDepth
-= n
; }
408 // See doc comment above for usage.
409 using Bump
= BumpRelease
;
411 inline std::string
indent() {
412 return std::string(indentDepth
, ' ');
415 template<typename
... Args
>
416 inline void itraceImpl(const char* fmtRaw
, Args
&&... args
) {
417 auto const fmt
= indent() + fmtRaw
;
418 Trace::ftraceRelease(fmt
, std::forward
<Args
>(args
)...);
420 #define ITRACE(level, ...) ONTRACE((level), Trace::itraceImpl(__VA_ARGS__));
421 #define ITRACE_MOD(mod, level, ...) \
422 ONTRACE_MOD(mod, level, Trace::itraceImpl(__VA_ARGS__));
424 void trace(ATTRIBUTE_PRINTF_STRING
const char *, ...) ATTRIBUTE_PRINTF(1,2);
425 void trace(const std::string
&);
427 template<typename Pretty
>
428 inline void trace(Pretty p
) { trace(p
.pretty() + std::string("\n")); }
430 void vtrace(ATTRIBUTE_PRINTF_STRING
const char *fmt
, va_list args
)
431 ATTRIBUTE_PRINTF(1,0);
432 void dumpRingbuffer();
434 // Ensure a tracing output file has been opened.
435 void ensureInit(std::string outFile
);
436 // Set tracing levels for this thread using a module:level,... specification.
437 // If traceSpec is empty, all levels for this thread are zeroed.
438 void setTraceThread(folly::StringPiece traceSpec
);
440 //////////////////////////////////////////////////////////////////////
442 #else /* } (!defined(NDEBUG) || defined(USE_TRACE)) { */
444 //////////////////////////////////////////////////////////////////////
446 * Implementation for when tracing is disabled.
449 #define ONTRACE(...) do { } while (0)
450 #define TRACE(...) do { } while (0)
451 #define FTRACE(...) do { } while (0)
452 #define ONTRACE_MOD(...) do { } while (0)
453 #define TRACE_MOD(...) do { } while (0)
454 #define FTRACE_MOD(...) do { } while (0)
455 #define TRACE_SET_MOD(name) \
456 DEBUG_ONLY static const HPHP::Trace::Module TRACEMOD = HPHP::Trace::name;
458 #define ITRACE(...) do { } while (0)
459 #define ITRACE_MOD(...) do { } while (0)
462 always_assert(true && "If this struct is completely empty we get unused "
463 "variable warnings in code that uses it.");
466 inline std::string
indent() {
467 return std::string();
471 Bump(Module
/*mod*/, int /*adjust*/, bool /*condition*/ = true) {
472 always_assert(true && "If this struct is completely empty we get unused "
473 "variable warnings in code that uses it.");
477 const bool enabled
= false;
479 inline void trace(const char*, ...) {}
480 inline void trace(const std::string
&) {}
481 inline void vtrace(const char*, va_list) {}
482 inline bool moduleEnabled(Module
/*t*/, int /*level*/ = 1) {
485 inline int moduleLevel(Module
/*tm*/) {
488 inline void ensureInit(std::string
/*outFile*/) {}
489 inline void setTraceThread(const std::string
& /*traceSpec*/) {}
491 //////////////////////////////////////////////////////////////////////
493 #endif /* } (!defined(NDEBUG) || defined(USE_TRACE)) */
497 // Optional color utility for trace dumps; when output is a tty or
498 // when we've been told to assume it is.
499 inline const char* color(const char* color
) {
500 static auto const shouldColorize
= []() -> bool {
501 auto const traceEnv
= getenv("HPHP_TRACE_FILE");
502 auto const assumeTTY
= getenv("HPHP_TRACE_TTY");
503 if (assumeTTY
) return true;
504 if (!traceEnv
) return false;
506 !strcmp(traceEnv
, "/dev/stdout") ? isatty(1) :
507 !strcmp(traceEnv
, "/dev/stderr") ? isatty(2) :
510 return shouldColorize
? color
: "";
513 inline std::string
color(const char* fg
, const char* bg
) {
514 auto const s
= add_bgcolor(fg
, bg
);
515 return color(s
.c_str());
518 //////////////////////////////////////////////////////////////////////
520 FOLLY_CREATE_MEMBER_INVOKER(invoke_toString
, toString
);
525 template<typename Val
>
526 class FormatValue
<Val
,
528 std::is_invocable_v
<HPHP::invoke_toString
, Val
const> &&
529 // This is here because MSVC decides that StringPiece matches
530 // both this overload as well as the FormatValue overload for
531 // string-y types in folly itself.
532 !std::is_same
<Val
, StringPiece
>::value
535 explicit FormatValue(const Val
& val
) : m_val(val
) {}
537 template<typename Callback
> void format(FormatArg
& arg
, Callback
& cb
) const {
538 format_value::formatString(m_val
.toString(), arg
, cb
);