2018-03-20 20:14:47 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
/* Implementation of an asynchronous lock-free logging system. */
|
|
|
|
|
|
|
|
#ifndef mozilla_dom_AsyncLogger_h
|
|
|
|
#define mozilla_dom_AsyncLogger_h
|
|
|
|
|
|
|
|
#include <atomic>
|
|
|
|
#include <thread>
|
2020-06-15 16:45:08 +03:00
|
|
|
#include <cinttypes>
|
2020-06-12 16:12:59 +03:00
|
|
|
#include "mozilla/ArrayUtils.h"
|
2018-03-20 20:14:47 +03:00
|
|
|
#include "mozilla/Attributes.h"
|
2020-06-12 16:12:59 +03:00
|
|
|
#include "mozilla/Logging.h"
|
2018-03-20 20:14:47 +03:00
|
|
|
#include "mozilla/MathAlgorithms.h"
|
|
|
|
#include "mozilla/Sprintf.h"
|
2020-06-12 16:12:59 +03:00
|
|
|
#include "GeckoProfiler.h"
|
|
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
|
|
# include "ProfilerMarkerPayload.h"
|
|
|
|
#endif
|
|
|
|
#include "MPSCQueue.h"
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
# include <process.h>
|
|
|
|
# define getpid() _getpid()
|
|
|
|
#else
|
|
|
|
# include <unistd.h>
|
|
|
|
#endif
|
2018-03-20 20:14:47 +03:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
2020-06-12 16:12:59 +03:00
|
|
|
const size_t PAYLOAD_TOTAL_SIZE = 2 << 9;
|
|
|
|
|
2018-03-20 20:14:47 +03:00
|
|
|
// This class implements a lock-free asynchronous logger, that outputs to
|
2020-06-12 16:12:59 +03:00
|
|
|
// MOZ_LOG or adds profiler markers (the default).
|
2018-03-20 20:14:47 +03:00
|
|
|
// Any thread can use this logger without external synchronization and without
|
|
|
|
// being blocked. This log is suitable for use in real-time audio threads.
|
|
|
|
// This class uses a thread internally, and must be started and stopped
|
|
|
|
// manually.
|
2020-06-12 16:12:59 +03:00
|
|
|
// If logging/profiling is disabled, all the calls are no-op and cheap.
|
2018-03-20 20:14:47 +03:00
|
|
|
class AsyncLogger {
|
|
|
|
public:
|
2020-06-12 16:11:47 +03:00
|
|
|
enum class TracingPhase : uint8_t { BEGIN, END, COMPLETE };
|
|
|
|
|
|
|
|
const char TRACING_PHASE_STRINGS[3] = {'B', 'E', 'X'};
|
|
|
|
|
|
|
|
enum AsyncLoggerOutputMode { MOZLOG, PROFILER };
|
2018-03-20 20:14:47 +03:00
|
|
|
|
2020-06-12 16:12:59 +03:00
|
|
|
struct TextPayload {
|
|
|
|
char mPayload[PAYLOAD_TOTAL_SIZE - MPSC_MSG_RESERVERD];
|
|
|
|
};
|
|
|
|
|
2020-06-25 15:33:31 +03:00
|
|
|
// On 32bits the struct is packed differently and there is a hole we need to
|
|
|
|
// account for.
|
2020-06-30 17:25:27 +03:00
|
|
|
#if !defined(HAVE_64BIT_BUILD) && \
|
|
|
|
!(defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
|
2020-06-25 15:33:31 +03:00
|
|
|
# define PADDING 8
|
|
|
|
#else
|
|
|
|
# define PADDING 0
|
|
|
|
#endif
|
|
|
|
|
2020-06-12 16:12:59 +03:00
|
|
|
// The order of the fields is important here to minimize padding.
|
|
|
|
struct TracePayload {
|
2020-06-25 02:08:43 +03:00
|
|
|
// If this marker is of phase B or E (begin or end), this is the time at
|
|
|
|
// which it was captured.
|
|
|
|
TimeStamp mTimestamp;
|
2020-06-25 15:33:31 +03:00
|
|
|
// If this marker is of phase X (COMPLETE), this holds the duration of the
|
|
|
|
// event in microseconds. Else, the value is not used.
|
|
|
|
uint32_t mDurationUs;
|
|
|
|
// The thread on which this tracepoint was gathered.
|
|
|
|
int mTID;
|
2020-06-12 16:12:59 +03:00
|
|
|
// An arbitrary string, usually containing a function signature or a
|
|
|
|
// recognizable tag of some sort, to be displayed when analyzing the
|
|
|
|
// profile.
|
|
|
|
char mName[PAYLOAD_TOTAL_SIZE - sizeof(TracingPhase) - sizeof(int) -
|
2020-06-25 15:33:31 +03:00
|
|
|
sizeof(uint32_t) - sizeof(TimeStamp) - MPSC_MSG_RESERVERD -
|
|
|
|
PADDING];
|
2020-06-12 16:12:59 +03:00
|
|
|
// A trace payload can be either:
|
|
|
|
// - Begin - this marks the beginning of a temporal region
|
|
|
|
// - End - this marks the end of a temporal region
|
|
|
|
// - Complete - this is a timestamp and a length, forming complete a
|
|
|
|
// temporal region
|
|
|
|
TracingPhase mPhase;
|
|
|
|
};
|
2020-06-25 15:33:31 +03:00
|
|
|
#undef PADDING
|
2020-07-03 15:24:37 +03:00
|
|
|
|
|
|
|
// The goal here is to make it easy on the allocator. We pack a pointer in the
|
|
|
|
// message struct, and we still want to do power of two allocations to
|
|
|
|
// minimize allocator slop.
|
|
|
|
#if !(defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID) && \
|
|
|
|
(defined(__arm__) || defined(__aarch64__)))
|
|
|
|
static_assert(sizeof(MPSCQueue<TracePayload>::Message) <= PAYLOAD_TOTAL_SIZE,
|
|
|
|
"MPSCQueue internal allocations too big.");
|
|
|
|
#endif
|
2018-03-20 20:14:47 +03:00
|
|
|
// aLogModuleName is the name of the MOZ_LOG module.
|
2020-06-12 16:11:47 +03:00
|
|
|
explicit AsyncLogger(const char* aLogModuleName,
|
|
|
|
AsyncLogger::AsyncLoggerOutputMode aMode =
|
|
|
|
AsyncLogger::AsyncLoggerOutputMode::PROFILER)
|
2020-06-12 16:12:59 +03:00
|
|
|
: mThread(nullptr),
|
|
|
|
mLogModule(aLogModuleName),
|
|
|
|
mRunning(false),
|
|
|
|
mMode(aMode) {}
|
2018-03-20 20:14:47 +03:00
|
|
|
|
|
|
|
void Start() {
|
|
|
|
MOZ_ASSERT(!mRunning, "Double calls to AsyncLogger::Start");
|
2020-06-12 16:12:59 +03:00
|
|
|
mRunning = true;
|
2020-06-12 16:11:47 +03:00
|
|
|
if (mMode == AsyncLogger::AsyncLoggerOutputMode::MOZLOG) {
|
|
|
|
LogMozLog("[");
|
|
|
|
}
|
2020-06-12 16:12:59 +03:00
|
|
|
Run();
|
2018-03-20 20:14:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Stop() {
|
2020-06-12 16:12:59 +03:00
|
|
|
if (mRunning) {
|
|
|
|
mRunning = false;
|
2018-03-20 20:14:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-12 16:12:50 +03:00
|
|
|
// Log something that has a beginning and an end
|
2020-06-12 16:11:47 +03:00
|
|
|
void Log(const char* aName, const char* aCategory, const char* aComment,
|
2020-06-12 16:12:50 +03:00
|
|
|
TracingPhase aPhase) {
|
2018-03-20 20:14:47 +03:00
|
|
|
if (Enabled()) {
|
2020-06-12 16:11:47 +03:00
|
|
|
if (mMode == AsyncLogger::AsyncLoggerOutputMode::MOZLOG) {
|
|
|
|
LogMozLog(
|
|
|
|
"{\"name\": \"%s\", \"cat\": \"%s\", \"ph\": \"%c\","
|
2020-06-12 16:12:50 +03:00
|
|
|
"\"ts\": %" PRIu64
|
|
|
|
", \"pid\": %d, \"tid\":"
|
|
|
|
" %zu, \"args\": { \"comment\": \"%s\"}},",
|
2020-06-12 16:11:47 +03:00
|
|
|
aName, aCategory, TRACING_PHASE_STRINGS[static_cast<int>(aPhase)],
|
2020-06-12 16:12:50 +03:00
|
|
|
NowInUs(), getpid(),
|
|
|
|
std::hash<std::thread::id>{}(std::this_thread::get_id()), aComment);
|
2020-06-12 16:11:47 +03:00
|
|
|
} else {
|
2020-06-12 16:12:59 +03:00
|
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
|
|
auto* msg = new MPSCQueue<TracePayload>::Message();
|
|
|
|
msg->data.mTID = profiler_current_thread_id();
|
|
|
|
msg->data.mPhase = aPhase;
|
|
|
|
msg->data.mTimestamp = TimeStamp::NowUnfuzzed();
|
|
|
|
msg->data.mDurationUs = 0; // unused, duration is end - begin
|
|
|
|
size_t len = std::min(strlen(aName), ArrayLength(msg->data.mName));
|
|
|
|
memcpy(msg->data.mName, aName, len);
|
|
|
|
msg->data.mName[len] = 0;
|
|
|
|
mMessageQueueProfiler.Push(msg);
|
|
|
|
#endif
|
2020-06-12 16:11:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-12 16:12:50 +03:00
|
|
|
// Log something that has a beginning and a duration
|
2020-06-12 16:11:47 +03:00
|
|
|
void LogDuration(const char* aName, const char* aCategory, uint64_t aDuration,
|
2020-06-12 16:12:50 +03:00
|
|
|
uint64_t aFrames, uint64_t aSampleRate) {
|
2020-06-12 16:11:47 +03:00
|
|
|
if (Enabled()) {
|
|
|
|
if (mMode == AsyncLogger::AsyncLoggerOutputMode::MOZLOG) {
|
|
|
|
LogMozLog(
|
|
|
|
"{\"name\": \"%s\", \"cat\": \"%s\", \"ph\": \"X\","
|
2020-06-12 16:12:50 +03:00
|
|
|
"\"ts\": %" PRIu64 ", \"dur\": %" PRIu64
|
|
|
|
", \"pid\": %d,"
|
|
|
|
"\"tid\": %zu, \"args\": { \"comment\": \"%" PRIu64 "/%" PRIu64
|
|
|
|
"\"}},",
|
2020-06-12 16:12:59 +03:00
|
|
|
aName, aCategory, NowInUs(), aDuration, getpid(),
|
|
|
|
std::hash<std::thread::id>{}(std::this_thread::get_id()), aFrames,
|
|
|
|
aSampleRate);
|
2020-06-12 16:11:47 +03:00
|
|
|
} else {
|
2020-06-12 16:12:59 +03:00
|
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
|
|
auto* msg = new MPSCQueue<TracePayload>::Message();
|
|
|
|
msg->data.mTID = profiler_current_thread_id();
|
|
|
|
msg->data.mPhase = TracingPhase::COMPLETE;
|
|
|
|
msg->data.mTimestamp = TimeStamp::NowUnfuzzed();
|
|
|
|
msg->data.mDurationUs =
|
|
|
|
(static_cast<double>(aFrames) / aSampleRate) * 1e6;
|
|
|
|
size_t len = std::min(strlen(aName), ArrayLength(msg->data.mName));
|
|
|
|
memcpy(msg->data.mName, aName, len);
|
|
|
|
msg->data.mName[len] = 0;
|
|
|
|
mMessageQueueProfiler.Push(msg);
|
|
|
|
#endif
|
2020-06-12 16:11:47 +03:00
|
|
|
}
|
2018-03-20 20:14:47 +03:00
|
|
|
}
|
|
|
|
}
|
2020-06-12 16:11:47 +03:00
|
|
|
void LogMozLog(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3) {
|
|
|
|
auto* msg = new MPSCQueue<TextPayload>::Message();
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
2020-06-12 16:12:59 +03:00
|
|
|
VsprintfLiteral(msg->data.mPayload, format, args);
|
2020-06-12 16:11:47 +03:00
|
|
|
va_end(args);
|
2020-06-12 16:12:59 +03:00
|
|
|
mMessageQueueLog.Push(msg);
|
2020-06-12 16:11:47 +03:00
|
|
|
}
|
2018-03-20 20:14:47 +03:00
|
|
|
|
|
|
|
bool Enabled() {
|
2020-06-12 16:12:59 +03:00
|
|
|
return (mMode == AsyncLoggerOutputMode::MOZLOG &&
|
|
|
|
MOZ_LOG_TEST(mLogModule, mozilla::LogLevel::Verbose))
|
|
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
|
|
|| (mMode == AsyncLoggerOutputMode::PROFILER && profiler_is_active())
|
|
|
|
#endif
|
|
|
|
;
|
2018-03-20 20:14:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void Run() {
|
|
|
|
mThread.reset(new std::thread([this]() {
|
2020-06-12 16:12:59 +03:00
|
|
|
PROFILER_REGISTER_THREAD("AsyncLogger");
|
2018-03-20 20:14:47 +03:00
|
|
|
while (mRunning) {
|
2020-06-12 16:12:59 +03:00
|
|
|
{
|
|
|
|
TextPayload message;
|
|
|
|
while (mMessageQueueLog.Pop(&message) && mRunning) {
|
|
|
|
MOZ_LOG(mLogModule, mozilla::LogLevel::Verbose,
|
|
|
|
("%s", message.mPayload));
|
|
|
|
}
|
2018-03-20 20:14:47 +03:00
|
|
|
}
|
2020-06-12 16:12:59 +03:00
|
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
|
|
{
|
|
|
|
TracePayload message;
|
|
|
|
while (mMessageQueueProfiler.Pop(&message) && mRunning) {
|
|
|
|
if (message.mPhase != TracingPhase::COMPLETE) {
|
|
|
|
TracingKind kind = message.mPhase == TracingPhase::BEGIN
|
|
|
|
? TracingKind::TRACING_INTERVAL_START
|
|
|
|
: TracingKind::TRACING_INTERVAL_END;
|
|
|
|
TracingMarkerPayload payload("media", kind, message.mTimestamp);
|
|
|
|
profiler_add_marker_for_thread(
|
|
|
|
message.mTID, JS::ProfilingCategoryPair::MEDIA_RT,
|
|
|
|
message.mName, payload);
|
|
|
|
} else {
|
|
|
|
mozilla::TimeStamp end =
|
|
|
|
message.mTimestamp +
|
2020-06-30 17:25:27 +03:00
|
|
|
TimeDuration::FromMicroseconds(message.mDurationUs);
|
|
|
|
BudgetMarkerPayload payload(message.mTimestamp, end);
|
2020-06-12 16:12:59 +03:00
|
|
|
profiler_add_marker_for_thread(
|
|
|
|
message.mTID, JS::ProfilingCategoryPair::MEDIA_RT,
|
2020-06-30 17:25:27 +03:00
|
|
|
message.mName, payload);
|
2020-06-12 16:12:59 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2018-03-20 20:14:47 +03:00
|
|
|
Sleep();
|
|
|
|
}
|
2020-06-12 16:12:59 +03:00
|
|
|
PROFILER_UNREGISTER_THREAD();
|
2018-03-20 20:14:47 +03:00
|
|
|
}));
|
2020-06-12 16:12:59 +03:00
|
|
|
// cleanup is done via mRunning
|
|
|
|
mThread->detach();
|
2018-03-20 20:14:47 +03:00
|
|
|
}
|
|
|
|
|
2020-06-12 16:11:47 +03:00
|
|
|
uint64_t NowInUs() {
|
|
|
|
static TimeStamp base = TimeStamp::NowUnfuzzed();
|
|
|
|
return (TimeStamp::NowUnfuzzed() - base).ToMicroseconds();
|
|
|
|
}
|
|
|
|
|
2018-03-20 20:14:47 +03:00
|
|
|
void Sleep() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
|
|
|
|
|
|
|
|
std::unique_ptr<std::thread> mThread;
|
|
|
|
mozilla::LazyLogModule mLogModule;
|
2020-06-12 16:12:59 +03:00
|
|
|
MPSCQueue<TextPayload> mMessageQueueLog;
|
|
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
|
|
MPSCQueue<TracePayload> mMessageQueueProfiler;
|
|
|
|
#endif
|
2018-03-20 20:14:47 +03:00
|
|
|
std::atomic<bool> mRunning;
|
2020-06-12 16:11:47 +03:00
|
|
|
std::atomic<AsyncLoggerOutputMode> mMode;
|
2018-03-20 20:14:47 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end namespace mozilla
|
|
|
|
|
2020-06-12 16:12:59 +03:00
|
|
|
#if defined(_WIN32)
|
|
|
|
# undef getpid
|
|
|
|
#endif
|
|
|
|
|
2018-03-20 20:14:47 +03:00
|
|
|
#endif // mozilla_dom_AsyncLogger_h
|