Skip to content
This repository was archived by the owner on May 1, 2023. It is now read-only.

Commit 7f32317

Browse files
Kui-Feng Leefacebook-github-bot
Kui-Feng Lee
authored andcommitted
Collect wall time and cpu time samples in the same trace.
Summary: Register new providers and create entries for CPU_STACK_SAMPLE and WALL_STACK_SAMPLE. Reviewed By: BurntBrunch Differential Revision: D32139331 fbshipit-source-id: 9e4cb70ad99e61bd6df145c80a62a99a0b253560
1 parent 1b74f51 commit 7f32317

File tree

15 files changed

+244
-52
lines changed

15 files changed

+244
-52
lines changed

cpp/codegen/config/android.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@
141141
"GPU_MEMORY_FREE",
142142
"THREAD_START",
143143
"THREAD_FINISH",
144+
"CPU_STACK_SAMPLE",
145+
"WALL_STACK_SAMPLE",
144146
]
145147

146148
STACK_FRAME_ENTRIES = frozenset(

cpp/generated/EntryType.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// @generated SignedSource<<b7eff2c1a5e497eb7eea5b61ad2d8f5f>>
1+
// @generated SignedSource<<390e60e658a65e09e8228eae35f255b4>>
22

33
#include <stdexcept>
44
#include <profilo/entries/EntryType.h>
@@ -123,6 +123,8 @@ const char* to_string(EntryType type) {
123123
case EntryType::GPU_MEMORY_FREE: return "GPU_MEMORY_FREE";
124124
case EntryType::THREAD_START: return "THREAD_START";
125125
case EntryType::THREAD_FINISH: return "THREAD_FINISH";
126+
case EntryType::CPU_STACK_SAMPLE: return "CPU_STACK_SAMPLE";
127+
case EntryType::WALL_STACK_SAMPLE: return "WALL_STACK_SAMPLE";
126128
default: throw std::invalid_argument("Unknown entry type");
127129
}
128130
}

cpp/generated/EntryType.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// @generated SignedSource<<a688ba6c4918a6c66ba16a8f17dc12d4>>
1+
// @generated SignedSource<<f0679634001e78402be9dd5db5850900>>
22

33
#pragma once
44

@@ -120,6 +120,8 @@ enum class EntryType {
120120
GPU_MEMORY_FREE = 111,
121121
THREAD_START = 112,
122122
THREAD_FINISH = 113,
123+
CPU_STACK_SAMPLE = 114,
124+
WALL_STACK_SAMPLE = 115,
123125
};
124126

125127

cpp/generated/EntryType.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// @generated SignedSource<<be259938f014b5fcd2b16c3412960680>>
1+
// @generated SignedSource<<5e4debd0d0a6279e0d17129e294db714>>
22

33
package com.facebook.profilo.entries;
44

@@ -118,6 +118,8 @@ public class EntryType {
118118
public static final int GPU_MEMORY_FREE = 111;
119119
public static final int THREAD_START = 112;
120120
public static final int THREAD_FINISH = 113;
121+
public static final int CPU_STACK_SAMPLE = 114;
122+
public static final int WALL_STACK_SAMPLE = 115;
121123

122124
public static final String[] NAMES = {
123125
"UNKNOWN_TYPE",
@@ -234,5 +236,7 @@ public class EntryType {
234236
"GPU_MEMORY_FREE",
235237
"THREAD_START",
236238
"THREAD_FINISH",
239+
"CPU_STACK_SAMPLE",
240+
"WALL_STACK_SAMPLE",
237241
};
238242
}

cpp/profiler/SamplingProfiler.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <profilo/profiler/ExternalTracer.h>
3939
#include <profilo/profiler/JavaBaseTracer.h>
4040
#include <profilo/profiler/Retcode.h>
41+
#include <profilo/profiler/ThreadTimer.h>
4142

4243
#include <profilo/LogEntry.h>
4344
#include <profilo/TraceProviders.h>
@@ -192,6 +193,8 @@ void SamplingProfiler::UnwindStackHandler(
192193
state.errStackOverflows.fetch_add(1);
193194
}
194195

196+
slot.timerType = ThreadTimer::decodeType(siginfo->si_value.sival_int);
197+
195198
// Ignore TRACER_DISABLED errors for now and free the slot.
196199
// TODO T42938550
197200
if (StackCollectionRetcode::TRACER_DISABLED == ret) {
@@ -288,6 +291,20 @@ void SamplingProfiler::unregisterSignalHandlers() {
288291
signal_handlers_.sigsegv->Disable();
289292
}
290293

294+
static int32_t
295+
logTimerType(StackSlot& slot, int32_t tid, MultiBufferLogger& logger) {
296+
auto type = slot.timerType == ThreadTimer::Type::CpuTime
297+
? EntryType::CPU_STACK_SAMPLE
298+
: EntryType::WALL_STACK_SAMPLE;
299+
auto id = logger.write(StandardEntry{
300+
.type = type,
301+
.timestamp = slot.time,
302+
.tid = tid,
303+
});
304+
305+
return id;
306+
}
307+
291308
void SamplingProfiler::flushStackTraces(
292309
std::unordered_set<uint64_t>& loggedFramesSet) {
293310
int processedCount = 0;
@@ -308,6 +325,8 @@ void SamplingProfiler::flushStackTraces(
308325
auto& tracer = state_.tracersMap[slot.profilerType];
309326
auto tid = slotStateCombo >> 16;
310327

328+
logTimerType(slot, tid, logger);
329+
311330
if (StackCollectionRetcode::SUCCESS == slotState) {
312331
tracer->flushStack(logger, slot.frames, slot.depth, tid, slot.time);
313332
} else {
@@ -429,6 +448,7 @@ bool SamplingProfiler::startProfilingTimers() {
429448
state_.timerManager.reset(new TimerManager(
430449
state_.threadDetectIntervalMs,
431450
state_.samplingRateMs,
451+
state_.cpuClockModeEnabled,
432452
state_.wallClockModeEnabled,
433453
state_.wallClockModeEnabled ? state_.whitelist : nullptr));
434454
state_.timerManager->start();
@@ -445,6 +465,7 @@ bool SamplingProfiler::startProfiling(
445465
int requested_tracers,
446466
int sampling_rate_ms,
447467
int thread_detect_interval_ms,
468+
bool cpu_clock_mode_enabled,
448469
bool wall_clock_mode_enabled) {
449470
if (state_.isProfiling) {
450471
throw std::logic_error("startProfiling called while already profiling");
@@ -467,6 +488,7 @@ bool SamplingProfiler::startProfiling(
467488
}
468489

469490
state_.samplingRateMs = sampling_rate_ms;
491+
state_.cpuClockModeEnabled = cpu_clock_mode_enabled;
470492
state_.wallClockModeEnabled = wall_clock_mode_enabled;
471493
state_.threadDetectIntervalMs = thread_detect_interval_ms;
472494

cpp/profiler/SamplingProfiler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct StackSlot {
6767
int64_t time;
6868
sigjmp_buf sig_jmp_buf;
6969
uint32_t profilerType;
70+
ThreadTimer::Type timerType;
7071
int64_t frames[MAX_STACK_DEPTH]; // frame pointer addresses
7172
char const* method_names[MAX_STACK_DEPTH];
7273
char const* class_descriptors[MAX_STACK_DEPTH];
@@ -106,6 +107,7 @@ struct ProfileState {
106107
std::atomic_bool isLoggerLoopDone;
107108

108109
// Config parameters
110+
bool cpuClockModeEnabled;
109111
bool wallClockModeEnabled;
110112
int threadDetectIntervalMs;
111113
int samplingRateMs;
@@ -146,6 +148,7 @@ class SamplingProfiler {
146148
int requested_providers,
147149
int sampling_rate_ms,
148150
int thread_detect_interval_ms,
151+
bool cpu_clock_mode_enabled,
149152
bool wall_clock_mode_enabled);
150153

151154
void addToWhitelist(int targetThread);

cpp/profiler/ThreadTimer.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <fb/log.h>
2323
#include <profilo/util/common.h>
2424
#include <random>
25+
#include <stdexcept>
2526
#include <system_error>
2627

2728
namespace facebook {
@@ -76,7 +77,9 @@ bool createThreadTimer(
7677
sigev.sigev_notify = SIGEV_THREAD_ID;
7778
sigev.sigev_signo = SIGPROF;
7879
sigev._sigev_un._tid = ktid; /* ID of kernel thread to signal */
79-
sigev.sigev_value.sival_ptr = nullptr;
80+
sigev.sigev_value.sival_int = ThreadTimer::encodeType(
81+
wallClockModeEnabled ? ThreadTimer::Type::WallTime
82+
: ThreadTimer::Type::CpuTime);
8083
if (timer_create(clockid, &sigev, timerId) != 0) {
8184
return false;
8285
}
@@ -136,14 +139,9 @@ itimerval getInitialItimerval(int samplingRateMs) {
136139
return tv;
137140
}
138141

139-
ThreadTimer::ThreadTimer(
140-
int32_t tid,
141-
int samplingRateMs,
142-
bool wallClockModeEnabled)
143-
: tid_(tid),
144-
samplingRateMs_(samplingRateMs),
145-
wallClockModeEnabled_(wallClockModeEnabled) {
146-
if (!createThreadTimer(tid_, &timerId_, wallClockModeEnabled_)) {
142+
ThreadTimer::ThreadTimer(int32_t tid, int samplingRateMs, Type timerType)
143+
: tid_(tid), samplingRateMs_(samplingRateMs), timerType_(timerType) {
144+
if (!createThreadTimer(tid_, &timerId_, timerType == Type::WallTime)) {
147145
// e.g. tid died
148146
throw std::system_error(errno, std::system_category(), "createThreadTimer");
149147
}
@@ -161,6 +159,23 @@ ThreadTimer::~ThreadTimer() {
161159
deleteThreadTimer(timerId_);
162160
}
163161

162+
long ThreadTimer::typeSeed = 0;
163+
164+
ThreadTimer::Type ThreadTimer::decodeType(long salted) {
165+
auto type = static_cast<Type>(salted ^ typeSeed);
166+
if (type != Type::CpuTime && type != Type::WallTime) {
167+
throw std::runtime_error("invalid timer type");
168+
}
169+
return type;
170+
}
171+
172+
long ThreadTimer::encodeType(Type type) {
173+
while (typeSeed == 0) {
174+
typeSeed = rand();
175+
}
176+
return static_cast<long>(type) ^ typeSeed;
177+
}
178+
164179
} // namespace profiler
165180
} // namespace profilo
166181
} // namespace facebook

cpp/profiler/ThreadTimer.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,32 @@ static const timer_t INVALID_TIMER_ID =
3131

3232
class ThreadTimer {
3333
public:
34-
explicit ThreadTimer(
35-
int32_t tid,
36-
int samplingRateMs,
37-
bool wallClockModeEnabled);
34+
enum class Type {
35+
CpuTime = 1,
36+
WallTime,
37+
};
38+
39+
explicit ThreadTimer(int32_t tid, int samplingRateMs, Type timerType);
3840
~ThreadTimer();
3941

4042
ThreadTimer(const ThreadTimer&) = delete;
4143
ThreadTimer& operator=(const ThreadTimer&) = delete;
4244
ThreadTimer(ThreadTimer&& other) noexcept // move constructor
4345
: tid_(other.tid_),
4446
samplingRateMs_(other.samplingRateMs_),
45-
wallClockModeEnabled_(other.wallClockModeEnabled_),
47+
timerType_(other.timerType_),
4648
timerId_(std::exchange(other.timerId_, INVALID_TIMER_ID)) {}
4749

50+
static Type decodeType(long salted);
51+
52+
static long encodeType(Type type);
53+
4854
private:
4955
int32_t tid_;
5056
int samplingRateMs_;
51-
bool wallClockModeEnabled_;
57+
Type timerType_;
5258
timer_t timerId_;
59+
static long typeSeed;
5360
};
5461

5562
} // namespace profiler

cpp/profiler/TimerManager.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,20 @@ void TimerManager::updateThreadTimers() {
9696
continue;
9797
}
9898
try {
99-
bool ok =
100-
state_.threadTimers
101-
.emplace(
102-
tid,
103-
ThreadTimer(
104-
tid, state_.samplingRateMs, state_.wallClockModeEnabled))
105-
.second;
106-
if (!ok) {
107-
FBLOGE("state_.threadTimers.insert failed");
99+
std::vector<ThreadTimer> timers;
100+
if (state_.cpuClockModeEnabled) {
101+
timers.emplace_back(ThreadTimer(
102+
tid, state_.samplingRateMs, ThreadTimer::Type::CpuTime));
103+
}
104+
if (state_.wallClockModeEnabled) {
105+
timers.emplace_back(ThreadTimer(
106+
tid, state_.samplingRateMs, ThreadTimer::Type::WallTime));
107+
}
108+
if (timers.size() > 0) {
109+
bool ok = state_.threadTimers.emplace(tid, std::move(timers)).second;
110+
if (!ok) {
111+
FBLOGE("state_.threadTimers.insert failed");
112+
}
108113
}
109114
} catch (const std::system_error& e) {
110115
// thread may have ended
@@ -153,10 +158,12 @@ void TimerManager::threadDetectLoop() {
153158
TimerManager::TimerManager(
154159
int threadDetectIntervalMs,
155160
int samplingRateMs,
161+
bool cpuClockModeEnabled,
156162
bool wallClockModeEnabled,
157163
std::shared_ptr<Whitelist> whitelist) {
158164
state_.threadDetectIntervalMs = threadDetectIntervalMs;
159165
state_.samplingRateMs = samplingRateMs;
166+
state_.cpuClockModeEnabled = cpuClockModeEnabled;
160167
state_.wallClockModeEnabled = wallClockModeEnabled;
161168
state_.whitelist = whitelist;
162169

cpp/profiler/TimerManager.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <unordered_map>
3232
#include <unordered_set>
3333
#include <utility>
34+
#include <vector>
3435

3536
namespace facebook {
3637
namespace profilo {
@@ -41,6 +42,7 @@ struct Whitelist;
4142
struct TimerManagerState {
4243
int threadDetectIntervalMs;
4344
int samplingRateMs;
45+
bool cpuClockModeEnabled;
4446
bool wallClockModeEnabled;
4547

4648
// whitelist is optional; use null for "all threads"
@@ -49,14 +51,15 @@ struct TimerManagerState {
4951
std::thread threadDetectThread;
5052
sem_t threadDetectSem;
5153
std::atomic_bool isThreadDetectLoopDone;
52-
std::unordered_map<pid_t, ThreadTimer> threadTimers;
54+
std::unordered_map<pid_t, std::vector<ThreadTimer>> threadTimers;
5355
};
5456

5557
class TimerManager {
5658
public:
5759
explicit TimerManager(
5860
int threadDetectIntervalMs,
5961
int samplingRateMs,
62+
bool cpuClockModeEnabled,
6063
bool wallClockModeEnabled,
6164
std::shared_ptr<Whitelist> whitelist);
6265
~TimerManager() = default;

cpp/profiler/jni.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,13 @@ static jboolean nativeStartProfiling(
158158
jint requested_tracers,
159159
jint sampling_rate_ms,
160160
jint thread_detect_interval_ms,
161+
jboolean cpu_clock_mode,
161162
jboolean wall_clock_mode) {
162163
return SamplingProfiler::getInstance().startProfiling(
163164
requested_tracers,
164165
sampling_rate_ms,
165166
thread_detect_interval_ms,
167+
cpu_clock_mode,
166168
wall_clock_mode);
167169
}
168170

0 commit comments

Comments
 (0)