fail-fast: false
matrix:
include:
- - name: Linux GCC debug + in source + tracing
+ - name: Linux GCC debug + in source
os: ubuntu-20.04
CC: gcc
CXX: g++
BUILDDIR: .
CCACHE_LOC: .
- CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=Debug -DENABLE_TRACING=1
+ CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=Debug
apt_get: elfutils libzstd-dev pkg-config libhiredis-dev
- name: Linux GCC 32-bit
# doesn't get scanned.
#
include(CodeAnalysis)
-option(ENABLE_TRACING "Enable possibility to use internal ccache tracing" OFF)
#
# Source code
----
-=== src/third_party/minitrace.*
-
-A library for producing JSON traces suitable for Chrome's built-in trace viewer
-(chrome://tracing). Downloaded from <https://github.com/hrydgard/minitrace>.
-
-----
-The MIT License (MIT)
-
-Copyright (c) 2014 Henrik Rydgård
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-----
-
-
=== src/third_party/nonstd/span.hpp
This is the single header version of
set(_DARWIN_C_SOURCE 1)
endif()
-# alias
-set(MTR_ENABLED "${ENABLE_TRACING}")
-
if(HAVE_SYS_MMAN_H
AND (HAVE_STRUCT_STAT_ST_MTIM OR HAVE_STRUCT_STAT_ST_MTIMESPEC)
AND (HAVE_LINUX_FS_H OR HAVE_STRUCT_STATFS_F_FSTYPENAME))
# pragma clang diagnostic pop
#endif
-#cmakedefine MTR_ENABLED
-
// === Header files ===
// Define if you have the <dirent.h> header file.
+++ /dev/null
-Developer manual
-================
-
-Tracing
--------
-
-In order to see what ccache is doing, it is possible to enable internal
-tracing:
-
-* Build ccache with the `-DENABLE_TRACING=1` cmake option.
-* Set the environment variable `CCACHE_INTERNAL_TRACE` to instruct ccache to
- create trace files at runtime.
-
-There will be one trace file per ccache invocation, named as the object file
-with a `.ccache-trace` suffix, e.g. `file.o.ccache-trace`. The trace file can
-then be loaded into the `chrome://tracing` page of Chromium/Chrome.
-
-You can combine several trace files into by using the `misc/combine-trace-files`
-script:
-
- misc/combine-trace-files *.o.ccache-trace | gzip > ccache.trace.gz
-
-(The gzip step is optional; Chrome supports both plain trace files and gzipped
-trace files.) The script will offset each individual trace by its start time in
-the combined file.
-
-There is also a script called `summarize-trace-files` that generates a summary
-(per job slot) of all the ccache runs:
-
- misc/combine-trace-files *.o.ccache-trace | misc/summarize-trace-files 4 > ccache.trace
-
-The script takes the number of job slots you used when building (e.g. `4` for
-`make -j4`) as the first argument.
+++ /dev/null
-#!/usr/bin/env python3
-
-import json
-import sys
-
-traces = {}
-for arg in sys.argv[1:]:
- events = json.load(open(arg))["traceEvents"]
- for event in events:
- if event["name"] == "" and event["ph"] == "I":
- time = float(event["args"]["time"])
- # print "%.6f" % time
- traces[time] = events
- break
-
-times = sorted(traces)
-min_time = min(times)
-
-combined_events = []
-for time in times:
- offset = (time - min_time) * 1000000.0
- events = traces[time]
- for event in events:
- event["ts"] = int(event["ts"] + offset)
- combined_events.append(event)
-
-print(json.dumps({"traceEvents": combined_events}))
list(APPEND source_files InodeCache.cpp)
endif()
-if(MTR_ENABLED)
- list(APPEND source_files MiniTrace.cpp)
-endif()
-
if(NOT WIN32)
list(APPEND source_files SignalHandler.cpp)
endif()
#include "Config.hpp"
-#include "MiniTrace.hpp"
#include "Util.hpp"
#include <core/AtomicFile.hpp>
set_system_config_path(env_ccache_configpath2
? env_ccache_configpath2
: Util::make_path(sysconfdir, "ccache.conf"));
- MTR_BEGIN("config", "conf_read_system");
// A missing config file in SYSCONFDIR is OK so don't check return value.
update_from_file(system_config_path());
- MTR_END("config", "conf_read_system");
const char* const env_ccache_dir = getenv("CCACHE_DIR");
auto cmdline_cache_dir = cmdline_settings_map.find("cache_dir");
const std::string& cache_dir_before_config_file_was_read = cache_dir();
- MTR_BEGIN("config", "conf_read");
update_from_file(config_path());
- MTR_END("config", "conf_read");
// Ignore cache_dir set in configuration file
set_cache_dir(cache_dir_before_config_file_was_read);
- MTR_BEGIN("config", "conf_update_from_environment");
update_from_environment();
// (cache_dir is set above if CCACHE_DIR is set.)
- MTR_END("config", "conf_update_from_environment");
update_from_map(cmdline_settings_map);
#include "Args.hpp"
#include "ArgsInfo.hpp"
#include "Config.hpp"
-#ifdef MTR_ENABLED
-# include "MiniTrace.hpp"
-#endif
#include <util/FileStream.hpp>
#include <util/NonCopyable.hpp>
// `nullopt` if there is no such configuration.
std::optional<mode_t> original_umask;
-#ifdef MTR_ENABLED
- // Internal tracing.
- std::unique_ptr<MiniTrace> mini_trace;
-#endif
-
// Whether we have added "/showIncludes" ourselves since it's missing and
// depend mode is enabled.
bool auto_depend_mode = false;
+++ /dev/null
-// Copyright (C) 2020-2023 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "MiniTrace.hpp"
-
-#include "ArgsInfo.hpp"
-
-#include <core/exceptions.hpp>
-#include <util/TemporaryFile.hpp>
-#include <util/TimePoint.hpp>
-#include <util/expected.hpp>
-#include <util/file.hpp>
-#include <util/filesystem.hpp>
-#include <util/format.hpp>
-#include <util/wincompat.hpp>
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-namespace fs = util::filesystem;
-
-MiniTrace::MiniTrace(const ArgsInfo& args_info)
- : m_args_info(args_info),
- m_trace_id(reinterpret_cast<void*>(getpid()))
-{
- auto tmp_dir = fs::temp_directory_path();
- if (!tmp_dir) {
- tmp_dir = "/tmp";
- }
- auto tmp_file = util::value_or_throw<core::Fatal>(
- util::TemporaryFile::create(*tmp_dir / "ccache-trace"));
- m_tmp_trace_file = tmp_file.path;
-
- mtr_init(m_tmp_trace_file.c_str());
- auto now = util::TimePoint::now();
- m_start_time = FMT("{}.{:06}", now.sec(), now.nsec_decimal_part() / 1000);
- MTR_INSTANT_C("", "", "time", m_start_time.c_str());
- MTR_META_PROCESS_NAME("ccache");
- MTR_START("program", "ccache", m_trace_id);
-}
-
-MiniTrace::~MiniTrace()
-{
- MTR_FINISH("program", "ccache", m_trace_id);
- mtr_flush();
- mtr_shutdown();
-
- if (!m_args_info.output_obj.empty()) {
- util::copy_file(m_tmp_trace_file, m_args_info.output_obj + ".ccache-trace");
- }
- util::remove(m_tmp_trace_file);
-}
+++ /dev/null
-// Copyright (C) 2020-2021 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include "third_party/minitrace.h"
-
-#include <string>
-
-struct ArgsInfo;
-
-class MiniTrace
-{
-public:
- MiniTrace(const ArgsInfo& args_info);
- ~MiniTrace();
-
-private:
- const ArgsInfo& m_args_info;
- const void* const m_trace_id;
- std::string m_tmp_trace_file;
- std::string m_start_time;
-};
#include "Context.hpp"
#include "Depfile.hpp"
#include "Hash.hpp"
-#include "MiniTrace.hpp"
#include "SignalHandler.hpp"
#include "Util.hpp"
#include "argprocessing.hpp"
ASSERT(ctx.config.direct_mode());
- MTR_SCOPE("manifest", "manifest_put");
-
// ctime() may be 0, so we have to check time_of_invocation against
// MAX(mtime, ctime).
//
}
LOG_RAW("Running real compiler");
- MTR_BEGIN("execute", "compiler");
tl::expected<DoExecuteResult, Failure> result;
if (!ctx.config.depend_mode()) {
result = do_execute(ctx, depend_mode_args);
}
- MTR_END("execute", "compiler");
if (!result) {
return tl::unexpected(result.error());
}
}
- MTR_BEGIN("result", "result_put");
if (!write_result(
ctx, *result_key, result->stdout_data, result->stderr_data)) {
return tl::unexpected(Statistic::compiler_produced_no_output);
}
- MTR_END("result", "result_put");
// Everything OK.
core::send_to_console(
add_prefix(args, ctx.config.prefix_command_cpp());
LOG_RAW("Running preprocessor");
- MTR_BEGIN("execute", "preprocessor");
const auto result = do_execute(ctx, args, false);
- MTR_END("execute", "preprocessor");
args.pop_back(args.size() - orig_args_size);
if (!result) {
static std::optional<Hash::Digest>
get_result_key_from_manifest(Context& ctx, const Hash::Digest& manifest_key)
{
- MTR_BEGIN("manifest", "manifest_get");
std::optional<Hash::Digest> result_key;
size_t read_manifests = 0;
ctx.storage.get(
return false;
}
});
- MTR_END("manifest", "manifest_get");
if (read_manifests > 1 && !ctx.config.remote_only()) {
- MTR_SCOPE("manifest", "merge");
LOG("Storing merged manifest {} locally",
util::format_digest(manifest_key));
core::CacheEntry::Header header(ctx.config, core::CacheEntryType::manifest);
return false;
}
- MTR_SCOPE("cache", "from_cache");
-
// Get result from cache.
util::Bytes cache_entry_data;
ctx.storage.get(
LOG("Configuration file: {}", ctx.config.config_path());
LOG("System configuration file: {}", ctx.config.system_config_path());
- if (getenv("CCACHE_INTERNAL_TRACE")) {
-#ifdef MTR_ENABLED
- ctx.mini_trace = std::make_unique<MiniTrace>(ctx.args_info);
-#else
- LOG_RAW("Error: tracing is not enabled!");
-#endif
- }
-
if (!ctx.config.log_file().empty() || ctx.config.debug()) {
ctx.config.visit_items([&ctx](const std::string& key,
const std::string& value,
ctx.storage.initialize();
- MTR_BEGIN("main", "find_compiler");
find_compiler(ctx, &find_executable, masquerading_as_compiler);
- MTR_END("main", "find_compiler");
// Guess compiler after logging the config value in order to be able to
// display "compiler_type = auto" before overwriting the value with the
// be disabled.
util::setenv("CCACHE_DISABLE", "1");
- MTR_BEGIN("main", "process_args");
ProcessArgsResult processed = process_args(ctx);
- MTR_END("main", "process_args");
if (processed.error) {
return tl::unexpected(*processed.error);
}
LOG("Object file: {}", ctx.args_info.output_obj);
- MTR_META_THREAD_NAME(ctx.args_info.output_obj.c_str());
if (ctx.config.debug() && ctx.config.debug_level() >= 2) {
const auto path = prepare_debug_path(ctx.apparent_cwd,
return tl::unexpected(Statistic::disabled);
}
- {
- MTR_SCOPE("hash", "common_hash");
- TRY(hash_common_info(
- ctx, processed.preprocessor_args, common_hash, ctx.args_info));
- }
+ TRY(hash_common_info(
+ ctx, processed.preprocessor_args, common_hash, ctx.args_info));
if (processed.hash_actual_cwd) {
common_hash.hash_delimiter("actual_cwd");
if (ctx.config.direct_mode()) {
LOG_RAW("Trying direct lookup");
- MTR_BEGIN("hash", "direct_hash");
const auto result_and_manifest_key = calculate_result_and_manifest_key(
ctx, args_to_hash, direct_hash, nullptr);
- MTR_END("hash", "direct_hash");
if (!result_and_manifest_key) {
return tl::unexpected(result_and_manifest_key.error());
}
Hash cpp_hash = common_hash;
init_hash_debug(ctx, cpp_hash, 'p', "PREPROCESSOR MODE", debug_text_file);
- MTR_BEGIN("hash", "cpp_hash");
const auto result_and_manifest_key = calculate_result_and_manifest_key(
ctx, args_to_hash, cpp_hash, &processed.preprocessor_args);
- MTR_END("hash", "cpp_hash");
if (!result_and_manifest_key) {
return tl::unexpected(result_and_manifest_key.error());
}
return tl::unexpected(from_cache_result.error());
} else if (*from_cache_result) {
if (ctx.config.direct_mode() && manifest_key && put_result_in_manifest) {
- MTR_SCOPE("cache", "update_manifest");
update_manifest(ctx, *manifest_key, *result_key);
}
return Statistic::preprocessed_cache_hit;
Hash* depend_mode_hash = ctx.config.depend_mode() ? &direct_hash : nullptr;
// Run real compiler, sending output to cache.
- MTR_BEGIN("cache", "to_cache");
const auto digest = to_cache(ctx,
processed.compiler_args,
result_key,
ctx.args_info.depend_extra_args,
depend_mode_hash);
- MTR_END("cache", "to_cache");
if (!digest) {
return tl::unexpected(digest.error());
}
result_key = *digest;
if (ctx.config.direct_mode()) {
ASSERT(manifest_key);
- MTR_SCOPE("cache", "update_manifest");
update_manifest(ctx, *manifest_key, *result_key);
}
#include "Storage.hpp"
#include <Config.hpp>
-#include <MiniTrace.hpp>
#include <Util.hpp>
#include <core/CacheEntry.hpp>
#include <core/Statistic.hpp>
const core::CacheEntryType type,
const EntryReceiver& entry_receiver)
{
- MTR_SCOPE("storage", "get");
-
if (!m_config.remote_only()) {
auto value = local.get(key, type);
if (value) {
const core::CacheEntryType type,
nonstd::span<const uint8_t> value)
{
- MTR_SCOPE("storage", "put");
-
if (!m_config.remote_only()) {
local.put(key, type, value);
}
void
Storage::remove(const Hash::Digest& key, const core::CacheEntryType type)
{
- MTR_SCOPE("storage", "remove");
-
if (!m_config.remote_only()) {
local.remove(key, type);
}
const core::CacheEntryType type,
const EntryReceiver& entry_receiver)
{
- MTR_SCOPE("remote_storage", "get");
-
for (const auto& entry : m_remote_storages) {
auto backend = get_backend(*entry, key, "getting from", false);
if (!backend) {
nonstd::span<const uint8_t> value,
bool only_if_missing)
{
- MTR_SCOPE("remote_storage", "put");
-
if (!core::CacheEntry::Header(value).self_contained) {
LOG("Not putting {} in remote storage since it's not self-contained",
util::format_digest(key));
void
Storage::remove_from_remote_storage(const Hash::Digest& key)
{
- MTR_SCOPE("remote_storage", "remove");
-
for (const auto& entry : m_remote_storages) {
auto backend = get_backend(*entry, key, "removing from", true);
if (!backend) {
#include <Config.hpp>
#include <Context.hpp>
-#include <MiniTrace.hpp>
#include <core/AtomicFile.hpp>
#include <core/CacheEntry.hpp>
#include <core/FileRecompressor.hpp>
std::optional<util::Bytes>
LocalStorage::get(const Hash::Digest& key, const core::CacheEntryType type)
{
- MTR_SCOPE("local_storage", "get");
-
std::optional<util::Bytes> return_value;
const auto cache_file = look_up_cache_file(key, type);
nonstd::span<const uint8_t> value,
bool only_if_missing)
{
- MTR_SCOPE("local_storage", "put");
-
const auto cache_file = look_up_cache_file(key, type);
if (only_if_missing && cache_file.dir_entry.exists()) {
LOG("Not storing {} in local storage since it already exists",
void
LocalStorage::remove(const Hash::Digest& key, const core::CacheEntryType type)
{
- MTR_SCOPE("local_storage", "remove");
-
const auto cache_file = look_up_cache_file(key, type);
if (!cache_file.dir_entry) {
LOG("No {} to remove from local storage", util::format_digest(key));
void
LocalStorage::clean_internal_tempdir()
{
- MTR_SCOPE("local_storage", "clean_internal_tempdir");
-
const auto now = util::TimePoint::now();
const auto cleaned_stamp = FMT("{}/.cleaned", m_config.temporary_dir());
DirEntry cleaned_dir_entry(cleaned_stamp);
target_sources(third_party PRIVATE win32/mktemp.c)
endif ()
-if(ENABLE_TRACING)
- target_sources(third_party PRIVATE minitrace.c)
-endif()
-
file(GLOB headers *.h fmt/*.h nonstd/*.hpp win32/*.h)
target_sources(third_party PRIVATE ${headers})
+++ /dev/null
-// minitrace
-// Copyright 2014 by Henrik Rydgård
-// http://www.github.com/hrydgard/minitrace
-// Released under the MIT license.
-
-// See minitrace.h for basic documentation.
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#ifdef _WIN32
-#pragma warning (disable:4996)
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#define __thread __declspec(thread)
-#define pthread_mutex_t CRITICAL_SECTION
-#define pthread_mutex_init(a, b) InitializeCriticalSection(a)
-#define pthread_mutex_lock(a) EnterCriticalSection(a)
-#define pthread_mutex_unlock(a) LeaveCriticalSection(a)
-#define pthread_mutex_destroy(a) DeleteCriticalSection(a)
-#else
-#include <signal.h>
-#include <pthread.h>
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-#include "minitrace.h"
-
-#ifdef __GNUC__
-#define ATTR_NORETURN __attribute__((noreturn))
-#else
-#define ATTR_NORETURN
-#endif
-
-#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
-#define TRUE 1
-#define FALSE 0
-
-// Ugh, this struct is already pretty heavy.
-// Will probably need to move arguments to a second buffer to support more than one.
-typedef struct raw_event {
- const char *name;
- const char *cat;
- void *id;
- int64_t ts;
- uint32_t pid;
- uint32_t tid;
- char ph;
- mtr_arg_type arg_type;
- const char *arg_name;
- union {
- const char *a_str;
- int a_int;
- double a_double;
- };
-} raw_event_t;
-
-static raw_event_t *event_buffer;
-static raw_event_t *flush_buffer;
-static volatile int event_count;
-static int is_tracing = FALSE;
-static int is_flushing = FALSE;
-static int events_in_progress = 0;
-static int64_t time_offset;
-static int first_line = 1;
-static FILE *f;
-static __thread int cur_thread_id; // Thread local storage
-static int cur_process_id;
-static pthread_mutex_t mutex;
-static pthread_mutex_t event_mutex;
-
-#define STRING_POOL_SIZE 100
-static char *str_pool[100];
-
-// forward declaration
-void mtr_flush_with_state(int);
-
-// Tiny portability layer.
-// Exposes:
-// get_cur_thread_id()
-// get_cur_process_id()
-// mtr_time_s()
-// pthread basics
-#ifdef _WIN32
-static int get_cur_thread_id() {
- return (int)GetCurrentThreadId();
-}
-static int get_cur_process_id() {
- return (int)GetCurrentProcessId();
-}
-
-static uint64_t _frequency = 0;
-static uint64_t _starttime = 0;
-double mtr_time_s() {
- if (_frequency == 0) {
- QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
- QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
- }
- __int64 time;
- QueryPerformanceCounter((LARGE_INTEGER*)&time);
- return ((double) (time - _starttime) / (double) _frequency);
-}
-
-// Ctrl+C handling for Windows console apps
-static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
- if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
- printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
- mtr_flush();
- mtr_shutdown();
- }
- ExitProcess(1);
-}
-
-void mtr_register_sigint_handler() {
- // For console apps:
- SetConsoleCtrlHandler(&CtrlHandler, TRUE);
-}
-
-#else
-
-static inline int get_cur_thread_id() {
- return (int)(intptr_t)pthread_self();
-}
-static inline int get_cur_process_id() {
- return (int)getpid();
-}
-
-#if defined(BLACKBERRY)
-double mtr_time_s() {
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
- return time.tv_sec + time.tv_nsec / 1.0e9;
-}
-#else
-double mtr_time_s() {
- static time_t start;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- if (start == 0) {
- start = tv.tv_sec;
- }
- tv.tv_sec -= start;
- return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
-}
-#endif // !BLACKBERRY
-
-static void termination_handler(int signum) ATTR_NORETURN;
-static void termination_handler(int signum) {
- (void) signum;
- if (is_tracing) {
- printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
- mtr_flush();
- fwrite("\n]}\n", 1, 4, f);
- fclose(f);
- }
- exit(1);
-}
-
-void mtr_register_sigint_handler() {
-#ifndef MTR_ENABLED
- return;
-#endif
- // Avoid altering set-to-be-ignored handlers while registering.
- if (signal(SIGINT, &termination_handler) == SIG_IGN)
- signal(SIGINT, SIG_IGN);
-}
-
-#endif
-
-void mtr_init_from_stream(void *stream) {
-#ifndef MTR_ENABLED
- return;
-#endif
- event_buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
- flush_buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
- is_tracing = 1;
- event_count = 0;
- f = (FILE *)stream;
- const char *header = "{\"traceEvents\":[\n";
- fwrite(header, 1, strlen(header), f);
- time_offset = (uint64_t)(mtr_time_s() * 1000000);
- first_line = 1;
- pthread_mutex_init(&mutex, 0);
- pthread_mutex_init(&event_mutex, 0);
-}
-
-void mtr_init(const char *json_file) {
-#ifndef MTR_ENABLED
- return;
-#endif
- mtr_init_from_stream(fopen(json_file, "wb"));
-}
-
-void mtr_shutdown() {
- int i;
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- is_tracing = FALSE;
- pthread_mutex_unlock(&mutex);
- mtr_flush_with_state(TRUE);
-
- fwrite("\n]}\n", 1, 4, f);
- fclose(f);
- pthread_mutex_destroy(&mutex);
- pthread_mutex_destroy(&event_mutex);
- f = 0;
- free(event_buffer);
- event_buffer = 0;
- for (i = 0; i < STRING_POOL_SIZE; i++) {
- if (str_pool[i]) {
- free(str_pool[i]);
- str_pool[i] = 0;
- }
- }
-}
-
-const char *mtr_pool_string(const char *str) {
- int i;
- for (i = 0; i < STRING_POOL_SIZE; i++) {
- if (!str_pool[i]) {
- str_pool[i] = (char*)malloc(strlen(str) + 1);
- strcpy(str_pool[i], str);
- return str_pool[i];
- } else {
- if (!strcmp(str, str_pool[i]))
- return str_pool[i];
- }
- }
- return "string pool full";
-}
-
-void mtr_start() {
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- is_tracing = TRUE;
- pthread_mutex_unlock(&mutex);
-}
-
-void mtr_stop() {
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- is_tracing = FALSE;
- pthread_mutex_unlock(&mutex);
-}
-
-// TODO: fwrite more than one line at a time.
-// Flushing is thread safe and process async
-// using double-buffering mechanism.
-// Aware: only one flushing process may be
-// running at any point of time
-void mtr_flush_with_state(int is_last) {
-#ifndef MTR_ENABLED
- return;
-#endif
- int i = 0;
- char linebuf[1024];
- char arg_buf[1024];
- char id_buf[256];
- int event_count_copy = 0;
- int events_in_progress_copy = 1;
- raw_event_t *event_buffer_tmp = NULL;
-
- // small critical section to swap buffers
- // - no any new events can be spawn while
- // swapping since they tied to the same mutex
- // - checks for any flushing in process
- pthread_mutex_lock(&mutex);
- // if not flushing already
- if (is_flushing) {
- pthread_mutex_unlock(&mutex);
- return;
- }
- is_flushing = TRUE;
- event_count_copy = event_count;
- event_buffer_tmp = flush_buffer;
- flush_buffer = event_buffer;
- event_buffer = event_buffer_tmp;
- event_count = 0;
- // waiting for any unfinished events before swap
- while (events_in_progress_copy != 0) {
- pthread_mutex_lock(&event_mutex);
- events_in_progress_copy = events_in_progress;
- pthread_mutex_unlock(&event_mutex);
- }
- pthread_mutex_unlock(&mutex);
-
- for (i = 0; i < event_count_copy; i++) {
- raw_event_t *raw = &flush_buffer[i];
- int len;
- switch (raw->arg_type) {
- case MTR_ARG_TYPE_INT:
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
- break;
- case MTR_ARG_TYPE_STRING_CONST:
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
- break;
- case MTR_ARG_TYPE_STRING_COPY:
- if (strlen(raw->a_str) > 700) {
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%.*s\"", raw->arg_name, 700, raw->a_str);
- } else {
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
- }
- break;
- case MTR_ARG_TYPE_NONE:
- arg_buf[0] = '\0';
- break;
- }
- if (raw->id) {
- switch (raw->ph) {
- case 'S':
- case 'T':
- case 'F':
- // TODO: Support full 64-bit pointers
- snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
- break;
- case 'X':
- snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
- break;
- }
- } else {
- id_buf[0] = 0;
- }
- const char *cat = raw->cat;
-#ifdef _WIN32
- // On Windows, we often end up with backslashes in category.
- char temp[256];
- {
- int len = (int)strlen(cat);
- int i;
- if (len > 255) len = 255;
- for (i = 0; i < len; i++) {
- temp[i] = cat[i] == '\\' ? '/' : cat[i];
- }
- temp[len] = 0;
- cat = temp;
- }
-#endif
-
- len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
- first_line ? "" : ",\n",
- cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
- fwrite(linebuf, 1, len, f);
- first_line = 0;
-
- if (raw->arg_type == MTR_ARG_TYPE_STRING_COPY) {
- free((void*)raw->a_str);
- }
- #ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
- free(raw->name);
- free(raw->cat);
- #endif
- }
-
- pthread_mutex_lock(&mutex);
- is_flushing = is_last;
- pthread_mutex_unlock(&mutex);
-}
-
-void mtr_flush() {
- mtr_flush_with_state(FALSE);
-}
-
-void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- if (!is_tracing || event_count >= INTERNAL_MINITRACE_BUFFER_SIZE) {
- pthread_mutex_unlock(&mutex);
- return;
- }
- raw_event_t *ev = &event_buffer[event_count];
- ++event_count;
- pthread_mutex_lock(&event_mutex);
- ++events_in_progress;
- pthread_mutex_unlock(&event_mutex);
- pthread_mutex_unlock(&mutex);
-
- double ts = mtr_time_s();
- if (!cur_thread_id) {
- cur_thread_id = get_cur_thread_id();
- }
- if (!cur_process_id) {
- cur_process_id = get_cur_process_id();
- }
-
-#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
- const size_t category_len = strlen(category);
- ev->cat = malloc(category_len + 1);
- strcpy(ev->cat, category);
-
- const size_t name_len = strlen(name);
- ev->name = malloc(name_len + 1);
- strcpy(ev->name, name);
-
-#else
- ev->cat = category;
- ev->name = name;
-#endif
-
- ev->id = id;
- ev->ph = ph;
- if (ev->ph == 'X') {
- double x;
- memcpy(&x, id, sizeof(double));
- ev->ts = (int64_t)(x * 1000000);
- ev->a_double = (ts - x) * 1000000;
- } else {
- ev->ts = (int64_t)(ts * 1000000);
- }
- ev->tid = cur_thread_id;
- ev->pid = cur_process_id;
- ev->arg_type = MTR_ARG_TYPE_NONE;
-
- pthread_mutex_lock(&event_mutex);
- --events_in_progress;
- pthread_mutex_unlock(&event_mutex);
-}
-
-void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value) {
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- if (!is_tracing || event_count >= INTERNAL_MINITRACE_BUFFER_SIZE) {
- pthread_mutex_unlock(&mutex);
- return;
- }
- raw_event_t *ev = &event_buffer[event_count];
- ++event_count;
- pthread_mutex_lock(&event_mutex);
- ++events_in_progress;
- pthread_mutex_unlock(&event_mutex);
- pthread_mutex_unlock(&mutex);
-
- if (!cur_thread_id) {
- cur_thread_id = get_cur_thread_id();
- }
- if (!cur_process_id) {
- cur_process_id = get_cur_process_id();
- }
- double ts = mtr_time_s();
-
-#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
- const size_t category_len = strlen(category);
- ev->cat = malloc(category_len + 1);
- strcpy(ev->cat, category);
-
- const size_t name_len = strlen(name);
- ev->name = malloc(name_len + 1);
- strcpy(ev->name, name);
-
-#else
- ev->cat = category;
- ev->name = name;
-#endif
-
- ev->id = id;
- ev->ts = (int64_t)(ts * 1000000);
- ev->ph = ph;
- ev->tid = cur_thread_id;
- ev->pid = cur_process_id;
- ev->arg_type = arg_type;
- ev->arg_name = arg_name;
- switch (arg_type) {
- case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
- case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
- case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
- case MTR_ARG_TYPE_NONE: break;
- }
-
- pthread_mutex_lock(&event_mutex);
- --events_in_progress;
- pthread_mutex_unlock(&event_mutex);
-}
-
+++ /dev/null
-// Minitrace
-//
-// Copyright 2014 by Henrik Rydgård
-// http://www.github.com/hrydgard/minitrace
-// Released under the MIT license.
-//
-// Ultra-light dependency free library for performance tracing C/C++ applications.
-// Produces traces compatible with Google Chrome's trace viewer.
-// Simply open "about:tracing" in Chrome and load the produced JSON.
-//
-// This contains far less template magic than the original libraries from Chrome
-// because this is meant to be usable from C.
-//
-// See README.md for a tutorial.
-//
-// The trace format is documented here:
-// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
-// More:
-// http://www.altdevblogaday.com/2012/08/21/using-chrometracing-to-view-your-inline-profiling-data/
-
-#ifndef MINITRACE_H
-#define MINITRACE_H
-
-#include <inttypes.h>
-
-// If MTR_ENABLED is not defined, Minitrace does nothing and has near zero overhead.
-// Preferably, set this flag in your build system. If you can't just uncomment this line.
-// #define MTR_ENABLED
-
-// By default, will collect up to 1000000 events, then you must flush.
-// It's recommended that you simply call mtr_flush on a background thread
-// occasionally. It's safe...ish.
-#define INTERNAL_MINITRACE_BUFFER_SIZE 1000000
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Initializes Minitrace. Must be called very early during startup of your executable,
-// before any MTR_ statements.
-void mtr_init(const char *json_file);
-// Same as above, but allows passing in a custom stream (FILE *), as returned by
-// fopen(). It should be opened for writing, preferably in binary mode to avoid
-// processing of line endings (i.e. the "wb" mode).
-void mtr_init_from_stream(void *stream);
-
-// Shuts down minitrace cleanly, flushing the trace buffer.
-void mtr_shutdown(void);
-
-// Lets you enable and disable Minitrace at runtime.
-// May cause strange discontinuities in the output.
-// Minitrace is enabled on startup by default.
-void mtr_start(void);
-void mtr_stop(void);
-
-// Flushes the collected data to disk, clearing the buffer for new data.
-void mtr_flush(void);
-
-// Returns the current time in seconds. Used internally by Minitrace. No caching.
-double mtr_time_s(void);
-
-// Registers a handler that will flush the trace on Ctrl+C.
-// Works on Linux and MacOSX, and in Win32 console applications.
-void mtr_register_sigint_handler(void);
-
-// Utility function that should rarely be used.
-// If str is semi dynamic, store it permanently in a small pool so we don't need to malloc it.
-// The pool fills up fast though and performance isn't great.
-// Returns a fixed string if the pool is full.
-const char *mtr_pool_string(const char *str);
-
-// Commented-out types will be supported in the future.
-typedef enum {
- MTR_ARG_TYPE_NONE = 0,
- MTR_ARG_TYPE_INT = 1, // I
- // MTR_ARG_TYPE_FLOAT = 2, // TODO
- // MTR_ARG_TYPE_DOUBLE = 3, // TODO
- MTR_ARG_TYPE_STRING_CONST = 8, // C
- MTR_ARG_TYPE_STRING_COPY = 9,
- // MTR_ARG_TYPE_JSON_COPY = 10,
-} mtr_arg_type;
-
-// TODO: Add support for more than one argument (metadata) per event
-// Having more costs speed and memory.
-#define MTR_MAX_ARGS 1
-
-// Only use the macros to call these.
-void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id);
-void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value);
-
-#ifdef MTR_ENABLED
-
-// c - category. Can be filtered by in trace viewer (or at least that's the intention).
-// A good use is to pass __FILE__, there are macros further below that will do it for you.
-// n - name. Pass __FUNCTION__ in most cases, unless you are marking up parts of one.
-
-// Scopes. In C++, use MTR_SCOPE. In C, always match them within the same scope.
-#define MTR_BEGIN(c, n) internal_mtr_raw_event(c, n, 'B', 0)
-#define MTR_END(c, n) internal_mtr_raw_event(c, n, 'E', 0)
-#define MTR_SCOPE(c, n) MTRScopedTrace ____mtr_scope(c, n)
-#define MTR_SCOPE_LIMIT(c, n, l) MTRScopedTraceLimit ____mtr_scope(c, n, l)
-
-// Async events. Can span threads. ID identifies which events to connect in the view.
-#define MTR_START(c, n, id) internal_mtr_raw_event(c, n, 'S', (void *)(id))
-#define MTR_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 'T', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
-#define MTR_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'F', (void *)(id))
-
-// Flow events. Like async events, but displayed in a more fancy way in the viewer.
-#define MTR_FLOW_START(c, n, id) internal_mtr_raw_event(c, n, 's', (void *)(id))
-#define MTR_FLOW_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 't', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
-#define MTR_FLOW_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'f', (void *)(id))
-
-// The same macros, but with a single named argument which shows up as metadata in the viewer.
-// _I for int.
-// _C is for a const string arg.
-// _S will copy the string, freeing on flush (expensive but sometimes necessary).
-// but required if the string was generated dynamically.
-
-// Note that it's fine to match BEGIN_S with END and BEGIN with END_S, etc.
-#define MTR_BEGIN_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_END_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_SCOPE_C(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-
-#define MTR_BEGIN_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-#define MTR_END_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-#define MTR_SCOPE_S(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-
-#define MTR_BEGIN_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-#define MTR_END_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-#define MTR_SCOPE_I(c, n, aname, aintval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-
-// Instant events. For things with no duration.
-#define MTR_INSTANT(c, n) internal_mtr_raw_event(c, n, 'I', 0)
-#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval))
-
-// Counters (can't do multi-value counters yet)
-#define MTR_COUNTER(c, n, val) internal_mtr_raw_event_arg(c, n, 'C', 0, MTR_ARG_TYPE_INT, n, (void *)(intptr_t)(val))
-
-// Metadata. Call at the start preferably. Must be const strings.
-
-#define MTR_META_PROCESS_NAME(n) internal_mtr_raw_event_arg("", "process_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
-#define MTR_META_THREAD_NAME(n) internal_mtr_raw_event_arg("", "thread_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
-#define MTR_META_THREAD_SORT_INDEX(i) internal_mtr_raw_event_arg("", "thread_sort_index", 'M', 0, MTR_ARG_TYPE_INT, "sort_index", (void *)(i))
-
-#else
-
-#define MTR_BEGIN(c, n)
-#define MTR_END(c, n)
-#define MTR_SCOPE(c, n)
-#define MTR_START(c, n, id)
-#define MTR_STEP(c, n, id, step)
-#define MTR_FINISH(c, n, id)
-#define MTR_FLOW_START(c, n, id)
-#define MTR_FLOW_STEP(c, n, id, step)
-#define MTR_FLOW_FINISH(c, n, id)
-#define MTR_INSTANT(c, n)
-
-#define MTR_BEGIN_C(c, n, aname, astrval)
-#define MTR_END_C(c, n, aname, astrval)
-#define MTR_SCOPE_C(c, n, aname, astrval)
-
-#define MTR_BEGIN_S(c, n, aname, astrval)
-#define MTR_END_S(c, n, aname, astrval)
-#define MTR_SCOPE_S(c, n, aname, astrval)
-
-#define MTR_BEGIN_I(c, n, aname, aintval)
-#define MTR_END_I(c, n, aname, aintval)
-#define MTR_SCOPE_I(c, n, aname, aintval)
-
-#define MTR_INSTANT(c, n)
-#define MTR_INSTANT_C(c, n, aname, astrval)
-#define MTR_INSTANT_I(c, n, aname, aintval)
-
-// Counters (can't do multi-value counters yet)
-#define MTR_COUNTER(c, n, val)
-
-// Metadata. Call at the start preferably. Must be const strings.
-
-#define MTR_META_PROCESS_NAME(n)
-
-#define MTR_META_THREAD_NAME(n)
-#define MTR_META_THREAD_SORT_INDEX(i)
-
-#endif
-
-// Shortcuts for simple function timing with automatic categories and names.
-
-#define MTR_BEGIN_FUNC() MTR_BEGIN(__FILE__, __FUNCTION__)
-#define MTR_END_FUNC() MTR_END(__FILE__, __FUNCTION__)
-#define MTR_SCOPE_FUNC() MTR_SCOPE(__FILE__, __FUNCTION__)
-#define MTR_INSTANT_FUNC() MTR_INSTANT(__FILE__, __FUNCTION__)
-#define MTR_SCOPE_FUNC_LIMIT_S(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, l)
-#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, (double)l * 0.000001)
-
-// Same, but with a single argument of the usual types.
-#define MTR_BEGIN_FUNC_S(aname, arg) MTR_BEGIN_S(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_S(aname, arg) MTR_END_S(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_S(aname, arg) MTR_SCOPE_S(__FILE__, __FUNCTION__, aname, arg)
-
-#define MTR_BEGIN_FUNC_C(aname, arg) MTR_BEGIN_C(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_C(aname, arg) MTR_END_C(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_C(aname, arg) MTR_SCOPE_C(__FILE__, __FUNCTION__, aname, arg)
-
-#define MTR_BEGIN_FUNC_I(aname, arg) MTR_BEGIN_I(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_I(aname, arg) MTR_END_I(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_I(aname, arg) MTR_SCOPE_I(__FILE__, __FUNCTION__, aname, arg)
-
-#ifdef __cplusplus
-}
-
-#ifdef MTR_ENABLED
-// These are optimized to use X events (combined B and E). Much easier to do in C++ than in C.
-class MTRScopedTrace {
-public:
- MTRScopedTrace(const char *category, const char *name)
- : category_(category), name_(name) {
- start_time_ = mtr_time_s();
- }
- ~MTRScopedTrace() {
- internal_mtr_raw_event(category_, name_, 'X', &start_time_);
- }
-
-private:
- const char *category_;
- const char *name_;
- double start_time_;
-};
-
-// Only outputs a block if execution time exceeded the limit.
-// TODO: This will effectively call mtr_time_s twice at the end, which is bad.
-class MTRScopedTraceLimit {
-public:
- MTRScopedTraceLimit(const char *category, const char *name, double limit_s)
- : category_(category), name_(name), limit_(limit_s) {
- start_time_ = mtr_time_s();
- }
- ~MTRScopedTraceLimit() {
- double end_time = mtr_time_s();
- if (end_time - start_time_ >= limit_) {
- internal_mtr_raw_event(category_, name_, 'X', &start_time_);
- }
- }
-
-private:
- const char *category_;
- const char *name_;
- double start_time_;
- double limit_;
-};
-
-class MTRScopedTraceArg {
-public:
- MTRScopedTraceArg(const char *category, const char *name, mtr_arg_type arg_type, const char *arg_name, void *arg_value)
- : category_(category), name_(name) {
- internal_mtr_raw_event_arg(category, name, 'B', 0, arg_type, arg_name, arg_value);
- }
- ~MTRScopedTraceArg() {
- internal_mtr_raw_event(category_, name_, 'E', 0);
- }
-
-private:
- const char *category_;
- const char *name_;
-};
-#endif
-
-#endif
-
-#endif