-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpu_sampler.cpp
More file actions
205 lines (174 loc) · 5.42 KB
/
cpu_sampler.cpp
File metadata and controls
205 lines (174 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include "cpu_sampler.h"
#include "utils.h"
#include <dlfcn.h>
#include <execinfo.h>
namespace {
std::vector<std::string> GetCallStackSymbols(const uint64_t *stack,
uint64_t depth) {
void *stackPointers[depth];
char **symbols;
std::vector<std::string> ret;
for (int i = 0; i < depth; ++i) {
stackPointers[i] = (void *)stack[i];
}
symbols = backtrace_symbols(stackPointers, depth);
if (symbols) {
for (int i = 0; i < depth; ++i) {
ret.push_back(ParseBTSymbol(std::string(symbols[i])));
// free(symbols[i]);
}
}
free(symbols);
return ret;
}
int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
int group_fd, uint64_t flags) {
return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
}
} // namespace
CPUCallStackSampler::CPUCallStackSampler(pid_t pid, uint64_t period,
uint64_t pages) {
struct perf_event_attr attr;
memset(&attr, 0, sizeof(struct perf_event_attr));
attr.size = sizeof(struct perf_event_attr);
// disable at init time
attr.disabled = 1;
attr.type = PERF_TYPE_SOFTWARE;
attr.config = PERF_COUNT_SW_CPU_CLOCK;
attr.sample_period = period;
attr.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_TID | PERF_SAMPLE_CALLCHAIN;
// notify every one overflow
attr.wakeup_events = 1;
fd = perf_event_open(&attr, pid, -1, -1, 0);
if (fd < 0) {
throw std::runtime_error("perf_event_open() failed");
}
// create a shared memory to read perf samples from kernel
mem = mmap(0, (1 + pages) * 4096, PROT_READ, MAP_SHARED, fd, 0);
if (mem == 0) {
throw std::runtime_error("mmap() failed");
}
this->pages = pages;
this->offset = 0;
}
CPUCallStackSampler::~CPUCallStackSampler() {
DisableSampling();
munmap(mem, (1 + pages) * 4096);
close(fd);
}
void CPUCallStackSampler::EnableSampling() {
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
}
void CPUCallStackSampler::DisableSampling() {
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
}
int CPUCallStackSampler::CollectData(int32_t timeout, uint64_t maxDepth,
struct CallStack &callStack) {
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
uint64_t start = Timer::GetMilliSeconds();
while (true) {
uint64_t now = Timer::GetMilliSeconds();
int32_t toWait;
if (timeout < 0) {
toWait = -1;
} else {
toWait = timeout - (int32_t)(now - start);
if (toWait < 0) {
// timeout
return -1;
}
}
int ret = poll(&pfd, 1, toWait);
if (ret == 0) {
return -1;
} else if (ret == -1) {
return errno;
}
struct sample {
struct perf_event_header header;
uint32_t pid, tid;
uint64_t time;
uint64_t nr;
uint64_t pcs[0];
} *sample = (struct sample *)((uint8_t *)mem + 4096 + offset);
struct perf_event_mmap_page *info = (struct perf_event_mmap_page *)mem;
offset = info->data_head % (pages * 4096);
if (sample->header.type != PERF_RECORD_SAMPLE) {
continue;
}
callStack.time = sample->time;
callStack.pid = sample->pid;
callStack.tid = sample->tid;
callStack.depth = min2(maxDepth, sample->nr);
callStack.pcs = sample->pcs;
callStack.fnames = GetCallStackSymbols(callStack.pcs, callStack.depth);
return 0;
}
}
CPUCallStackSamplerCollection::~CPUCallStackSamplerCollection() {
for (auto itr : samplers) {
delete itr.second;
}
}
void CPUCallStackSamplerCollection::RegisterSampler(pid_t pid) {
if (samplers.find(pid) == samplers.end()) {
auto sampler = GetOrCreateCPUCallStackSampler(pid);
samplers.insert({pid, sampler});
}
}
// TODO(yanli): buggy? delete here but not in the static map in
// GetOrCreateCPUCallStackSampler.
void CPUCallStackSamplerCollection::DeleteSampler(pid_t pid) {
auto itr = samplers.find(pid);
if (itr != samplers.end()) {
delete itr->second;
samplers.erase(pid);
} else {
DEBUG_LOG("sampler %d does not exist\n", pid);
}
}
void CPUCallStackSamplerCollection::EnableSampling() {
statusMutex.lock();
for (auto itr : samplers) {
itr.second->EnableSampling();
}
running = true;
statusMutex.unlock();
}
void CPUCallStackSamplerCollection::DisableSampling() {
statusMutex.lock();
for (auto itr : samplers) {
itr.second->DisableSampling();
}
running = false;
statusMutex.unlock();
}
bool CPUCallStackSamplerCollection::IsRunning() { return running; }
std::unordered_map<pid_t, CPUCallStackSampler::CallStack>
CPUCallStackSamplerCollection::CollectData() {
statusMutex.lock();
std::unordered_map<pid_t, CPUCallStackSampler::CallStack> ret;
for (auto itr : samplers) {
CPUCallStackSampler::CallStack callStack;
itr.second->CollectData(GetProfilerConf()->cpuSamplingTimeout,
GetProfilerConf()->cpuSamplingMaxDepth, callStack);
ret.insert({itr.first, callStack});
}
statusMutex.unlock();
return ret;
}
CPUCallStackSampler *GetOrCreateCPUCallStackSampler(pid_t pid) {
auto profilerConf = GetProfilerConf();
//TODO(lpc0220): no deletion of this samplerMap?
static std::unordered_map<pid_t, CPUCallStackSampler *> samplerMap;
if (samplerMap.find(pid) != samplerMap.end()) {
return samplerMap[pid];
} else {
samplerMap.insert(
{pid, new CPUCallStackSampler(pid, profilerConf->cpuSamplingPeriod,
profilerConf->cpuSamplingPages)});
return samplerMap[pid];
}
}