Changeset View
Changeset View
Standalone View
Standalone View
llvm/lib/Support/TimeProfiler.cpp
//===-- TimeProfiler.cpp - Hierarchical Time Profiler ---------------------===// | //===-- TimeProfiler.cpp - Hierarchical Time Profiler ---------------------===// | ||||
// | // | ||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||
// See https://llvm.org/LICENSE.txt for license information. | // See https://llvm.org/LICENSE.txt for license information. | ||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||
// | // | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
// | // | ||||
// This file implements hierarchical time profiler. | // This file implements hierarchical time profiler. | ||||
// | // | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
#include "llvm/Support/TimeProfiler.h" | #include "llvm/Support/TimeProfiler.h" | ||||
#include "llvm/ADT/STLFunctionalExtras.h" | #include "llvm/ADT/STLFunctionalExtras.h" | ||||
#include "llvm/ADT/StringMap.h" | #include "llvm/ADT/StringMap.h" | ||||
#include "llvm/Support/JSON.h" | #include "llvm/Support/JSON.h" | ||||
#include "llvm/Support/ManagedStatic.h" | |||||
#include "llvm/Support/Path.h" | #include "llvm/Support/Path.h" | ||||
#include "llvm/Support/Process.h" | #include "llvm/Support/Process.h" | ||||
#include "llvm/Support/Threading.h" | #include "llvm/Support/Threading.h" | ||||
#include <algorithm> | #include <algorithm> | ||||
#include <cassert> | #include <cassert> | ||||
#include <chrono> | #include <chrono> | ||||
#include <mutex> | #include <mutex> | ||||
#include <string> | #include <string> | ||||
#include <vector> | #include <vector> | ||||
using namespace std::chrono; | using namespace std::chrono; | ||||
using namespace llvm; | using namespace llvm; | ||||
static std::mutex Mu; | namespace { | ||||
// List of all instances | |||||
static ManagedStatic<std::vector<TimeTraceProfiler *>> | struct TimeTraceProfilerInstances { | ||||
ThreadTimeTraceProfilerInstances; // GUARDED_BY(Mu) | std::mutex Lock; | ||||
std::vector<TimeTraceProfiler *> List; | |||||
}; | |||||
TimeTraceProfilerInstances &getTimeTraceProfilerInstances() { | |||||
static TimeTraceProfilerInstances Instances; | |||||
return Instances; | |||||
} | |||||
} // anonymous namespace | |||||
// Per Thread instance | // Per Thread instance | ||||
static LLVM_THREAD_LOCAL TimeTraceProfiler *TimeTraceProfilerInstance = nullptr; | static LLVM_THREAD_LOCAL TimeTraceProfiler *TimeTraceProfilerInstance = nullptr; | ||||
TimeTraceProfiler *llvm::getTimeTraceProfilerInstance() { | TimeTraceProfiler *llvm::getTimeTraceProfilerInstance() { | ||||
return TimeTraceProfilerInstance; | return TimeTraceProfilerInstance; | ||||
} | } | ||||
typedef duration<steady_clock::rep, steady_clock::period> DurationType; | typedef duration<steady_clock::rep, steady_clock::period> DurationType; | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | void end() { | ||||
Stack.pop_back(); | Stack.pop_back(); | ||||
} | } | ||||
// Write events from this TimeTraceProfilerInstance and | // Write events from this TimeTraceProfilerInstance and | ||||
// ThreadTimeTraceProfilerInstances. | // ThreadTimeTraceProfilerInstances. | ||||
void write(raw_pwrite_stream &OS) { | void write(raw_pwrite_stream &OS) { | ||||
// Acquire Mutex as reading ThreadTimeTraceProfilerInstances. | // Acquire Mutex as reading ThreadTimeTraceProfilerInstances. | ||||
std::lock_guard<std::mutex> Lock(Mu); | auto &Instances = getTimeTraceProfilerInstances(); | ||||
std::lock_guard<std::mutex> Lock(Instances.Lock); | |||||
assert(Stack.empty() && | assert(Stack.empty() && | ||||
"All profiler sections should be ended when calling write"); | "All profiler sections should be ended when calling write"); | ||||
assert(llvm::all_of(*ThreadTimeTraceProfilerInstances, | assert(llvm::all_of(Instances.List, | ||||
[](const auto &TTP) { return TTP->Stack.empty(); }) && | [](const auto &TTP) { return TTP->Stack.empty(); }) && | ||||
"All profiler sections should be ended when calling write"); | "All profiler sections should be ended when calling write"); | ||||
json::OStream J(OS); | json::OStream J(OS); | ||||
J.objectBegin(); | J.objectBegin(); | ||||
J.attributeBegin("traceEvents"); | J.attributeBegin("traceEvents"); | ||||
J.arrayBegin(); | J.arrayBegin(); | ||||
Show All 11 Lines | auto writeEvent = [&](const auto &E, uint64_t Tid) { | ||||
J.attribute("name", E.Name); | J.attribute("name", E.Name); | ||||
if (!E.Detail.empty()) { | if (!E.Detail.empty()) { | ||||
J.attributeObject("args", [&] { J.attribute("detail", E.Detail); }); | J.attributeObject("args", [&] { J.attribute("detail", E.Detail); }); | ||||
} | } | ||||
}); | }); | ||||
}; | }; | ||||
for (const Entry &E : Entries) | for (const Entry &E : Entries) | ||||
writeEvent(E, this->Tid); | writeEvent(E, this->Tid); | ||||
for (const TimeTraceProfiler *TTP : *ThreadTimeTraceProfilerInstances) | for (const TimeTraceProfiler *TTP : Instances.List) | ||||
for (const Entry &E : TTP->Entries) | for (const Entry &E : TTP->Entries) | ||||
writeEvent(E, TTP->Tid); | writeEvent(E, TTP->Tid); | ||||
// Emit totals by section name as additional "thread" events, sorted from | // Emit totals by section name as additional "thread" events, sorted from | ||||
// longest one. | // longest one. | ||||
// Find highest used thread id. | // Find highest used thread id. | ||||
uint64_t MaxTid = this->Tid; | uint64_t MaxTid = this->Tid; | ||||
for (const TimeTraceProfiler *TTP : *ThreadTimeTraceProfilerInstances) | for (const TimeTraceProfiler *TTP : Instances.List) | ||||
MaxTid = std::max(MaxTid, TTP->Tid); | MaxTid = std::max(MaxTid, TTP->Tid); | ||||
// Combine all CountAndTotalPerName from threads into one. | // Combine all CountAndTotalPerName from threads into one. | ||||
StringMap<CountAndDurationType> AllCountAndTotalPerName; | StringMap<CountAndDurationType> AllCountAndTotalPerName; | ||||
auto combineStat = [&](const auto &Stat) { | auto combineStat = [&](const auto &Stat) { | ||||
StringRef Key = Stat.getKey(); | StringRef Key = Stat.getKey(); | ||||
auto Value = Stat.getValue(); | auto Value = Stat.getValue(); | ||||
auto &CountAndTotal = AllCountAndTotalPerName[Key]; | auto &CountAndTotal = AllCountAndTotalPerName[Key]; | ||||
CountAndTotal.first += Value.first; | CountAndTotal.first += Value.first; | ||||
CountAndTotal.second += Value.second; | CountAndTotal.second += Value.second; | ||||
}; | }; | ||||
for (const auto &Stat : CountAndTotalPerName) | for (const auto &Stat : CountAndTotalPerName) | ||||
combineStat(Stat); | combineStat(Stat); | ||||
for (const TimeTraceProfiler *TTP : *ThreadTimeTraceProfilerInstances) | for (const TimeTraceProfiler *TTP : Instances.List) | ||||
for (const auto &Stat : TTP->CountAndTotalPerName) | for (const auto &Stat : TTP->CountAndTotalPerName) | ||||
combineStat(Stat); | combineStat(Stat); | ||||
std::vector<NameAndCountAndDurationType> SortedTotals; | std::vector<NameAndCountAndDurationType> SortedTotals; | ||||
SortedTotals.reserve(AllCountAndTotalPerName.size()); | SortedTotals.reserve(AllCountAndTotalPerName.size()); | ||||
for (const auto &Total : AllCountAndTotalPerName) | for (const auto &Total : AllCountAndTotalPerName) | ||||
SortedTotals.emplace_back(std::string(Total.getKey()), Total.getValue()); | SortedTotals.emplace_back(std::string(Total.getKey()), Total.getValue()); | ||||
Show All 34 Lines | auto writeMetadataEvent = [&](const char *Name, uint64_t Tid, | ||||
J.attribute("ph", "M"); | J.attribute("ph", "M"); | ||||
J.attribute("name", Name); | J.attribute("name", Name); | ||||
J.attributeObject("args", [&] { J.attribute("name", arg); }); | J.attributeObject("args", [&] { J.attribute("name", arg); }); | ||||
}); | }); | ||||
}; | }; | ||||
writeMetadataEvent("process_name", Tid, ProcName); | writeMetadataEvent("process_name", Tid, ProcName); | ||||
writeMetadataEvent("thread_name", Tid, ThreadName); | writeMetadataEvent("thread_name", Tid, ThreadName); | ||||
for (const TimeTraceProfiler *TTP : *ThreadTimeTraceProfilerInstances) | for (const TimeTraceProfiler *TTP : Instances.List) | ||||
writeMetadataEvent("thread_name", TTP->Tid, TTP->ThreadName); | writeMetadataEvent("thread_name", TTP->Tid, TTP->ThreadName); | ||||
J.arrayEnd(); | J.arrayEnd(); | ||||
J.attributeEnd(); | J.attributeEnd(); | ||||
// Emit the absolute time when this TimeProfiler started. | // Emit the absolute time when this TimeProfiler started. | ||||
// This can be used to combine the profiling data from | // This can be used to combine the profiling data from | ||||
// multiple processes and preserve actual time intervals. | // multiple processes and preserve actual time intervals. | ||||
Show All 27 Lines | TimeTraceProfilerInstance = new TimeTraceProfiler( | ||||
TimeTraceGranularity, llvm::sys::path::filename(ProcName)); | TimeTraceGranularity, llvm::sys::path::filename(ProcName)); | ||||
} | } | ||||
// Removes all TimeTraceProfilerInstances. | // Removes all TimeTraceProfilerInstances. | ||||
// Called from main thread. | // Called from main thread. | ||||
void llvm::timeTraceProfilerCleanup() { | void llvm::timeTraceProfilerCleanup() { | ||||
delete TimeTraceProfilerInstance; | delete TimeTraceProfilerInstance; | ||||
TimeTraceProfilerInstance = nullptr; | TimeTraceProfilerInstance = nullptr; | ||||
std::lock_guard<std::mutex> Lock(Mu); | |||||
for (auto *TTP : *ThreadTimeTraceProfilerInstances) | auto &Instances = getTimeTraceProfilerInstances(); | ||||
std::lock_guard<std::mutex> Lock(Instances.Lock); | |||||
for (auto *TTP : Instances.List) | |||||
delete TTP; | delete TTP; | ||||
ThreadTimeTraceProfilerInstances->clear(); | Instances.List.clear(); | ||||
} | } | ||||
// Finish TimeTraceProfilerInstance on a worker thread. | // Finish TimeTraceProfilerInstance on a worker thread. | ||||
// This doesn't remove the instance, just moves the pointer to global vector. | // This doesn't remove the instance, just moves the pointer to global vector. | ||||
void llvm::timeTraceProfilerFinishThread() { | void llvm::timeTraceProfilerFinishThread() { | ||||
std::lock_guard<std::mutex> Lock(Mu); | auto &Instances = getTimeTraceProfilerInstances(); | ||||
ThreadTimeTraceProfilerInstances->push_back(TimeTraceProfilerInstance); | std::lock_guard<std::mutex> Lock(Instances.Lock); | ||||
Instances.List.push_back(TimeTraceProfilerInstance); | |||||
TimeTraceProfilerInstance = nullptr; | TimeTraceProfilerInstance = nullptr; | ||||
} | } | ||||
void llvm::timeTraceProfilerWrite(raw_pwrite_stream &OS) { | void llvm::timeTraceProfilerWrite(raw_pwrite_stream &OS) { | ||||
assert(TimeTraceProfilerInstance != nullptr && | assert(TimeTraceProfilerInstance != nullptr && | ||||
"Profiler object can't be null"); | "Profiler object can't be null"); | ||||
TimeTraceProfilerInstance->write(OS); | TimeTraceProfilerInstance->write(OS); | ||||
} | } | ||||
Show All 37 Lines |