blob: 847ffd6362d8101182ed9afe2ff8dca29d95563c [file] [log] [blame]
Yabin Cui6965d422016-06-15 11:41:42 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "command.h"
18
19#include <unordered_map>
20
21#include <android-base/logging.h>
22#include <android-base/strings.h>
23
24#include "callchain.h"
25#include "event_attr.h"
26#include "event_type.h"
27#include "record_file.h"
28#include "sample_tree.h"
29#include "tracing.h"
30#include "utils.h"
31
Yabin Cuifaa7b922021-01-11 17:35:57 -080032namespace simpleperf {
Yabin Cui6965d422016-06-15 11:41:42 -070033namespace {
34
35struct SlabSample {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020036 const Symbol* symbol; // the function making allocation
37 uint64_t ptr; // the start address of the allocated space
38 uint64_t bytes_req; // requested space size
39 uint64_t bytes_alloc; // allocated space size
40 uint64_t sample_count; // count of allocations
41 uint64_t gfp_flags; // flags used for allocation
42 uint64_t cross_cpu_allocations; // count of allocations freed not on the
43 // cpu allocating them
Yabin Cui6965d422016-06-15 11:41:42 -070044 CallChainRoot<SlabSample> callchain; // a callchain tree representing all
45 // callchains in this sample
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020046 SlabSample(const Symbol* symbol, uint64_t ptr, uint64_t bytes_req, uint64_t bytes_alloc,
47 uint64_t sample_count, uint64_t gfp_flags, uint64_t cross_cpu_allocations)
Yabin Cui6965d422016-06-15 11:41:42 -070048 : symbol(symbol),
49 ptr(ptr),
50 bytes_req(bytes_req),
51 bytes_alloc(bytes_alloc),
52 sample_count(sample_count),
53 gfp_flags(gfp_flags),
54 cross_cpu_allocations(cross_cpu_allocations) {}
Yabin Cui18385c42016-12-09 14:51:04 -080055
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020056 uint64_t GetPeriod() const { return sample_count; }
Yabin Cui6965d422016-06-15 11:41:42 -070057};
58
59struct SlabAccumulateInfo {
60 uint64_t bytes_req;
61 uint64_t bytes_alloc;
62};
63
64BUILD_COMPARE_VALUE_FUNCTION(ComparePtr, ptr);
65BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareBytesReq, bytes_req);
66BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareBytesAlloc, bytes_alloc);
67BUILD_COMPARE_VALUE_FUNCTION(CompareGfpFlags, gfp_flags);
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020068BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareCrossCpuAllocations, cross_cpu_allocations);
Yabin Cui6965d422016-06-15 11:41:42 -070069
70BUILD_DISPLAY_HEX64_FUNCTION(DisplayPtr, ptr);
71BUILD_DISPLAY_UINT64_FUNCTION(DisplayBytesReq, bytes_req);
72BUILD_DISPLAY_UINT64_FUNCTION(DisplayBytesAlloc, bytes_alloc);
73BUILD_DISPLAY_HEX64_FUNCTION(DisplayGfpFlags, gfp_flags);
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020074BUILD_DISPLAY_UINT64_FUNCTION(DisplayCrossCpuAllocations, cross_cpu_allocations);
Yabin Cui6965d422016-06-15 11:41:42 -070075
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020076static int CompareFragment(const SlabSample* sample1, const SlabSample* sample2) {
Yabin Cui6965d422016-06-15 11:41:42 -070077 uint64_t frag1 = sample1->bytes_alloc - sample1->bytes_req;
78 uint64_t frag2 = sample2->bytes_alloc - sample2->bytes_req;
79 return Compare(frag2, frag1);
80}
81
82static std::string DisplayFragment(const SlabSample* sample) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020083 return android::base::StringPrintf("%" PRIu64, sample->bytes_alloc - sample->bytes_req);
Yabin Cui6965d422016-06-15 11:41:42 -070084}
85
86struct SlabSampleTree {
87 std::vector<SlabSample*> samples;
88 uint64_t total_requested_bytes;
89 uint64_t total_allocated_bytes;
90 uint64_t nr_allocations;
91 uint64_t nr_frees;
92 uint64_t nr_cross_cpu_allocations;
93};
94
95struct SlabFormat {
96 enum {
97 KMEM_ALLOC,
98 KMEM_FREE,
99 } type;
100 TracingFieldPlace call_site;
101 TracingFieldPlace ptr;
102 TracingFieldPlace bytes_req;
103 TracingFieldPlace bytes_alloc;
104 TracingFieldPlace gfp_flags;
105};
106
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200107class SlabSampleTreeBuilder : public SampleTreeBuilder<SlabSample, SlabAccumulateInfo> {
Yabin Cui6965d422016-06-15 11:41:42 -0700108 public:
Chih-Hung Hsiehf6d5b0d2017-08-03 14:04:06 -0700109 SlabSampleTreeBuilder(const SampleComparator<SlabSample>& sample_comparator,
Yabin Cui6965d422016-06-15 11:41:42 -0700110 ThreadTree* thread_tree)
111 : SampleTreeBuilder(sample_comparator),
112 thread_tree_(thread_tree),
113 total_requested_bytes_(0),
114 total_allocated_bytes_(0),
115 nr_allocations_(0),
116 nr_cross_cpu_allocations_(0) {}
117
118 SlabSampleTree GetSampleTree() const {
119 SlabSampleTree sample_tree;
120 sample_tree.samples = GetSamples();
121 sample_tree.total_requested_bytes = total_requested_bytes_;
122 sample_tree.total_allocated_bytes = total_allocated_bytes_;
123 sample_tree.nr_allocations = nr_allocations_;
124 sample_tree.nr_frees = nr_frees_;
125 sample_tree.nr_cross_cpu_allocations = nr_cross_cpu_allocations_;
126 return sample_tree;
127 }
128
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200129 void AddSlabFormat(const std::vector<uint64_t>& event_ids, SlabFormat format) {
Yabin Cui6965d422016-06-15 11:41:42 -0700130 std::unique_ptr<SlabFormat> p(new SlabFormat(format));
131 for (auto id : event_ids) {
132 event_id_to_format_map_[id] = p.get();
133 }
134 formats_.push_back(std::move(p));
135 }
136
137 protected:
138 SlabSample* CreateSample(const SampleRecord& r, bool in_kernel,
139 SlabAccumulateInfo* acc_info) override {
140 if (!in_kernel) {
141 // Normally we don't parse records in user space because tracepoint
142 // events all happen in kernel. But if r.ip_data.ip == 0, it may be
143 // a kernel record failed to dump ip register and is still useful.
144 if (r.ip_data.ip == 0) {
145 // It seems we are on a kernel can't dump regset for tracepoint events
146 // because of lacking perf_arch_fetch_caller_regs(). We can't get
147 // callchain, but we can still do a normal report.
148 static bool first = true;
149 if (first) {
150 first = false;
151 if (accumulate_callchain_) {
152 // The kernel doesn't seem to support dumping registers for
153 // tracepoint events because of lacking
154 // perf_arch_fetch_caller_regs().
155 LOG(WARNING) << "simpleperf may not get callchains for tracepoint"
Yabin Cui9970a232016-06-29 12:18:11 -0700156 << " events because of lacking kernel support.";
Yabin Cui6965d422016-06-15 11:41:42 -0700157 }
158 }
159 } else {
160 return nullptr;
161 }
162 }
163 uint64_t id = r.id_data.id;
164 auto it = event_id_to_format_map_.find(id);
165 if (it == event_id_to_format_map_.end()) {
166 return nullptr;
167 }
Yabin Cui190a8482016-08-04 10:22:17 -0700168 const char* raw_data = r.raw_data.data;
Yabin Cui6965d422016-06-15 11:41:42 -0700169 SlabFormat* format = it->second;
170 if (format->type == SlabFormat::KMEM_ALLOC) {
171 uint64_t call_site = format->call_site.ReadFromData(raw_data);
172 const Symbol* symbol = thread_tree_->FindKernelSymbol(call_site);
173 uint64_t ptr = format->ptr.ReadFromData(raw_data);
174 uint64_t bytes_req = format->bytes_req.ReadFromData(raw_data);
175 uint64_t bytes_alloc = format->bytes_alloc.ReadFromData(raw_data);
176 uint64_t gfp_flags = format->gfp_flags.ReadFromData(raw_data);
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200177 SlabSample* sample = InsertSample(std::unique_ptr<SlabSample>(
178 new SlabSample(symbol, ptr, bytes_req, bytes_alloc, 1, gfp_flags, 0)));
179 alloc_cpu_record_map_.insert(std::make_pair(ptr, std::make_pair(r.cpu_data.cpu, sample)));
Yabin Cui6965d422016-06-15 11:41:42 -0700180 acc_info->bytes_req = bytes_req;
181 acc_info->bytes_alloc = bytes_alloc;
182 return sample;
183 } else if (format->type == SlabFormat::KMEM_FREE) {
184 uint64_t ptr = format->ptr.ReadFromData(raw_data);
185 auto it = alloc_cpu_record_map_.find(ptr);
186 if (it != alloc_cpu_record_map_.end()) {
187 SlabSample* sample = it->second.second;
188 if (r.cpu_data.cpu != it->second.first) {
189 sample->cross_cpu_allocations++;
190 nr_cross_cpu_allocations_++;
191 }
192 alloc_cpu_record_map_.erase(it);
193 }
194 nr_frees_++;
195 }
196 return nullptr;
197 }
198
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200199 SlabSample* CreateBranchSample(const SampleRecord&, const BranchStackItemType&) override {
Yabin Cui6965d422016-06-15 11:41:42 -0700200 return nullptr;
201 }
202
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200203 SlabSample* CreateCallChainSample(const ThreadEntry*, const SlabSample* sample, uint64_t ip,
204 bool in_kernel, const std::vector<SlabSample*>& callchain,
205 const SlabAccumulateInfo& acc_info) override {
Yabin Cui6965d422016-06-15 11:41:42 -0700206 if (!in_kernel) {
207 return nullptr;
208 }
209 const Symbol* symbol = thread_tree_->FindKernelSymbol(ip);
210 return InsertCallChainSample(
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200211 std::unique_ptr<SlabSample>(new SlabSample(symbol, sample->ptr, acc_info.bytes_req,
212 acc_info.bytes_alloc, 1, sample->gfp_flags, 0)),
Yabin Cui6965d422016-06-15 11:41:42 -0700213 callchain);
214 }
215
216 const ThreadEntry* GetThreadOfSample(SlabSample*) override { return nullptr; }
217
Yabin Cui9970a232016-06-29 12:18:11 -0700218 uint64_t GetPeriodForCallChain(const SlabAccumulateInfo&) override {
Yabin Cui6965d422016-06-15 11:41:42 -0700219 // Decide the percentage of callchain by the sample_count, so use 1 as the
220 // period when calling AddCallChain().
Yabin Cui9970a232016-06-29 12:18:11 -0700221 return 1;
Yabin Cui6965d422016-06-15 11:41:42 -0700222 }
223
224 void UpdateSummary(const SlabSample* sample) override {
225 total_requested_bytes_ += sample->bytes_req;
226 total_allocated_bytes_ += sample->bytes_alloc;
227 nr_allocations_++;
228 }
229
230 void MergeSample(SlabSample* sample1, SlabSample* sample2) override {
231 sample1->bytes_req += sample2->bytes_req;
232 sample1->bytes_alloc += sample2->bytes_alloc;
233 sample1->sample_count += sample2->sample_count;
234 }
235
236 private:
237 ThreadTree* thread_tree_;
238 uint64_t total_requested_bytes_;
239 uint64_t total_allocated_bytes_;
240 uint64_t nr_allocations_;
241 uint64_t nr_frees_;
242 uint64_t nr_cross_cpu_allocations_;
243
244 std::unordered_map<uint64_t, SlabFormat*> event_id_to_format_map_;
245 std::vector<std::unique_ptr<SlabFormat>> formats_;
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200246 std::unordered_map<uint64_t, std::pair<uint32_t, SlabSample*>> alloc_cpu_record_map_;
Yabin Cui6965d422016-06-15 11:41:42 -0700247};
248
249using SlabSampleTreeSorter = SampleTreeSorter<SlabSample>;
250using SlabSampleTreeDisplayer = SampleTreeDisplayer<SlabSample, SlabSampleTree>;
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200251using SlabSampleCallgraphDisplayer = CallgraphDisplayer<SlabSample, CallChainNode<SlabSample>>;
Yabin Cui6965d422016-06-15 11:41:42 -0700252
253struct EventAttrWithName {
254 perf_event_attr attr;
255 std::string name;
256 std::vector<uint64_t> event_ids;
257};
258
259class KmemCommand : public Command {
260 public:
261 KmemCommand()
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200262 : Command("kmem", "collect kernel memory allocation information",
263 // clang-format off
Yabin Cui6965d422016-06-15 11:41:42 -0700264"Usage: kmem (record [record options] | report [report options])\n"
265"kmem record\n"
266"-g Enable call graph recording. Same as '--call-graph fp'.\n"
267"--slab Collect slab allocation information. Default option.\n"
268"Other record options provided by simpleperf record command are also available.\n"
269"kmem report\n"
270"--children Print the accumulated allocation info appeared in the callchain.\n"
271" Can be used on perf.data recorded with `--call-graph fp` option.\n"
272"-g [callee|caller] Print call graph for perf.data recorded with\n"
273" `--call-graph fp` option. If callee mode is used, the graph\n"
274" shows how functions are called from others. Otherwise, the\n"
275" graph shows how functions call others. Default is callee\n"
276" mode. The percentage shown in the graph is determined by\n"
277" the hit count of the callchain.\n"
278"-i Specify path of record file, default is perf.data\n"
279"-o report_file_name Set report file name, default is stdout.\n"
280"--slab Report slab allocation information. Default option.\n"
281"--slab-sort key1,key2,...\n"
282" Select the keys to sort and print slab allocation information.\n"
283" Should be used with --slab option. Possible keys include:\n"
284" hit -- the allocation count.\n"
285" caller -- the function calling allocation.\n"
286" ptr -- the address of the allocated space.\n"
287" bytes_req -- the total requested space size.\n"
288" bytes_alloc -- the total allocated space size.\n"
289" fragment -- the extra allocated space size\n"
290" (bytes_alloc - bytes_req).\n"
291" gfp_flags -- the flags used for allocation.\n"
292" pingpong -- the count of allocations that are freed not on\n"
293" the cpu allocating them.\n"
294" The default slab sort keys are:\n"
295" hit,caller,bytes_req,bytes_alloc,fragment,pingpong.\n"
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200296 // clang-format on
297 ),
Yabin Cui6965d422016-06-15 11:41:42 -0700298 is_record_(false),
299 use_slab_(false),
300 accumulate_callchain_(false),
301 print_callgraph_(false),
302 callgraph_show_callee_(false),
303 record_filename_("perf.data"),
Yabin Cui38076912021-08-16 16:59:09 -0700304 record_file_arch_(GetTargetArch()) {}
Yabin Cui6965d422016-06-15 11:41:42 -0700305
306 bool Run(const std::vector<std::string>& args);
307
308 private:
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200309 bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* left_args);
Yabin Cui6965d422016-06-15 11:41:42 -0700310 bool RecordKmemInfo(const std::vector<std::string>& record_args);
311 bool ReportKmemInfo();
312 bool PrepareToBuildSampleTree();
313 void ReadEventAttrsFromRecordFile();
314 bool ReadFeaturesFromRecordFile();
315 bool ReadSampleTreeFromRecordFile();
316 bool ProcessRecord(std::unique_ptr<Record> record);
Yabin Cui540932c2022-09-13 15:52:33 -0700317 bool ProcessTracingData(const std::vector<char>& data);
Yabin Cui6965d422016-06-15 11:41:42 -0700318 bool PrintReport();
319 void PrintReportContext(FILE* fp);
320 void PrintSlabReportContext(FILE* fp);
321
322 bool is_record_;
323 bool use_slab_;
324 std::vector<std::string> slab_sort_keys_;
325 bool accumulate_callchain_;
326 bool print_callgraph_;
327 bool callgraph_show_callee_;
328
329 std::string record_filename_;
330 std::unique_ptr<RecordFileReader> record_file_reader_;
331 std::vector<EventAttrWithName> event_attrs_;
332 std::string record_cmdline_;
333 ArchType record_file_arch_;
334
335 ThreadTree thread_tree_;
336 SlabSampleTree slab_sample_tree_;
337 std::unique_ptr<SlabSampleTreeBuilder> slab_sample_tree_builder_;
338 std::unique_ptr<SlabSampleTreeSorter> slab_sample_tree_sorter_;
339 std::unique_ptr<SlabSampleTreeDisplayer> slab_sample_tree_displayer_;
340
341 std::string report_filename_;
342};
343
344bool KmemCommand::Run(const std::vector<std::string>& args) {
345 std::vector<std::string> left_args;
346 if (!ParseOptions(args, &left_args)) {
347 return false;
348 }
349 if (!use_slab_) {
350 use_slab_ = true;
351 }
352 if (is_record_) {
353 return RecordKmemInfo(left_args);
354 }
355 return ReportKmemInfo();
356}
357
358bool KmemCommand::ParseOptions(const std::vector<std::string>& args,
359 std::vector<std::string>* left_args) {
360 if (args.empty()) {
361 LOG(ERROR) << "No subcommand specified";
362 return false;
363 }
364 if (args[0] == "record") {
365 if (!IsRoot()) {
366 LOG(ERROR) << "simpleperf kmem record command needs root privilege";
367 return false;
368 }
369 is_record_ = true;
370 size_t i;
371 for (i = 1; i < args.size() && !args[i].empty() && args[i][0] == '-'; ++i) {
372 if (args[i] == "-g") {
373 left_args->push_back("--call-graph");
374 left_args->push_back("fp");
375 } else if (args[i] == "--slab") {
376 use_slab_ = true;
377 } else {
378 left_args->push_back(args[i]);
379 }
380 }
381 left_args->insert(left_args->end(), args.begin() + i, args.end());
382 } else if (args[0] == "report") {
383 is_record_ = false;
384 for (size_t i = 1; i < args.size(); ++i) {
385 if (args[i] == "--children") {
386 accumulate_callchain_ = true;
387 } else if (args[i] == "-g") {
388 print_callgraph_ = true;
389 accumulate_callchain_ = true;
390 callgraph_show_callee_ = true;
391 if (i + 1 < args.size() && args[i + 1][0] != '-') {
392 ++i;
393 if (args[i] == "callee") {
394 callgraph_show_callee_ = true;
395 } else if (args[i] == "caller") {
396 callgraph_show_callee_ = false;
397 } else {
398 LOG(ERROR) << "Unknown argument with -g option: " << args[i];
399 return false;
400 }
401 }
402 } else if (args[i] == "-i") {
403 if (!NextArgumentOrError(args, &i)) {
404 return false;
405 }
406 record_filename_ = args[i];
407 } else if (args[i] == "-o") {
408 if (!NextArgumentOrError(args, &i)) {
409 return false;
410 }
411 report_filename_ = args[i];
412 } else if (args[i] == "--slab") {
413 use_slab_ = true;
414 } else if (args[i] == "--slab-sort") {
415 if (!NextArgumentOrError(args, &i)) {
416 return false;
417 }
418 slab_sort_keys_ = android::base::Split(args[i], ",");
419 } else {
420 ReportUnknownOption(args, i);
421 return false;
422 }
423 }
424 } else {
425 LOG(ERROR) << "Unknown subcommand for " << Name() << ": " << args[0]
426 << ". Try `simpleperf help " << Name() << "`";
427 return false;
428 }
429 return true;
430}
431
432bool KmemCommand::RecordKmemInfo(const std::vector<std::string>& record_args) {
433 std::vector<std::string> args;
434 if (use_slab_) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200435 std::vector<std::string> trace_events = {"kmem:kmalloc", "kmem:kmem_cache_alloc",
436 "kmem:kmalloc_node", "kmem:kmem_cache_alloc_node",
437 "kmem:kfree", "kmem:kmem_cache_free"};
Yabin Cui6965d422016-06-15 11:41:42 -0700438 for (const auto& name : trace_events) {
439 if (ParseEventType(name)) {
440 args.insert(args.end(), {"-e", name});
441 }
442 }
443 }
444 if (args.empty()) {
445 LOG(ERROR) << "Kernel allocation related trace events are not supported.";
446 return false;
447 }
448 args.push_back("-a");
449 args.insert(args.end(), record_args.begin(), record_args.end());
450 std::unique_ptr<Command> record_cmd = CreateCommandInstance("record");
451 if (record_cmd == nullptr) {
452 LOG(ERROR) << "record command isn't available";
453 return false;
454 }
455 return record_cmd->Run(args);
456}
457
458bool KmemCommand::ReportKmemInfo() {
459 if (!PrepareToBuildSampleTree()) {
460 return false;
461 }
462 record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
463 if (record_file_reader_ == nullptr) {
464 return false;
465 }
466 ReadEventAttrsFromRecordFile();
467 if (!ReadFeaturesFromRecordFile()) {
468 return false;
469 }
470 if (!ReadSampleTreeFromRecordFile()) {
471 return false;
472 }
473 if (!PrintReport()) {
474 return false;
475 }
476 return true;
477}
478
479bool KmemCommand::PrepareToBuildSampleTree() {
480 if (use_slab_) {
481 if (slab_sort_keys_.empty()) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200482 slab_sort_keys_ = {"hit", "caller", "bytes_req", "bytes_alloc", "fragment", "pingpong"};
Yabin Cui6965d422016-06-15 11:41:42 -0700483 }
484 SampleComparator<SlabSample> comparator;
485 SampleComparator<SlabSample> sort_comparator;
486 SampleDisplayer<SlabSample, SlabSampleTree> displayer;
487 std::string accumulated_name = accumulate_callchain_ ? "Accumulated_" : "";
488
489 if (print_callgraph_) {
Yabin Cui9970a232016-06-29 12:18:11 -0700490 displayer.AddExclusiveDisplayFunction(SlabSampleCallgraphDisplayer());
Yabin Cui6965d422016-06-15 11:41:42 -0700491 }
492
493 for (const auto& key : slab_sort_keys_) {
494 if (key == "hit") {
495 sort_comparator.AddCompareFunction(CompareSampleCount);
Yabin Cui2da26a02021-12-21 15:47:05 -0800496 displayer.AddDisplayFunction(accumulated_name + "Hit", DisplaySampleCount<SlabSample>);
Yabin Cui6965d422016-06-15 11:41:42 -0700497 } else if (key == "caller") {
498 comparator.AddCompareFunction(CompareSymbol);
Yabin Cui2da26a02021-12-21 15:47:05 -0800499 displayer.AddDisplayFunction("Caller", DisplaySymbol<SlabSample>);
Yabin Cui6965d422016-06-15 11:41:42 -0700500 } else if (key == "ptr") {
501 comparator.AddCompareFunction(ComparePtr);
Yabin Cui2da26a02021-12-21 15:47:05 -0800502 displayer.AddDisplayFunction("Ptr", DisplayPtr<SlabSample>);
Yabin Cui6965d422016-06-15 11:41:42 -0700503 } else if (key == "bytes_req") {
504 sort_comparator.AddCompareFunction(CompareBytesReq);
Yabin Cui2da26a02021-12-21 15:47:05 -0800505 displayer.AddDisplayFunction(accumulated_name + "BytesReq", DisplayBytesReq<SlabSample>);
Yabin Cui6965d422016-06-15 11:41:42 -0700506 } else if (key == "bytes_alloc") {
507 sort_comparator.AddCompareFunction(CompareBytesAlloc);
Yabin Cui2da26a02021-12-21 15:47:05 -0800508 displayer.AddDisplayFunction(accumulated_name + "BytesAlloc",
509 DisplayBytesAlloc<SlabSample>);
Yabin Cui6965d422016-06-15 11:41:42 -0700510 } else if (key == "fragment") {
511 sort_comparator.AddCompareFunction(CompareFragment);
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200512 displayer.AddDisplayFunction(accumulated_name + "Fragment", DisplayFragment);
Yabin Cui6965d422016-06-15 11:41:42 -0700513 } else if (key == "gfp_flags") {
514 comparator.AddCompareFunction(CompareGfpFlags);
Yabin Cui2da26a02021-12-21 15:47:05 -0800515 displayer.AddDisplayFunction("GfpFlags", DisplayGfpFlags<SlabSample>);
Yabin Cui6965d422016-06-15 11:41:42 -0700516 } else if (key == "pingpong") {
517 sort_comparator.AddCompareFunction(CompareCrossCpuAllocations);
Yabin Cui2da26a02021-12-21 15:47:05 -0800518 displayer.AddDisplayFunction("Pingpong", DisplayCrossCpuAllocations<SlabSample>);
Yabin Cui6965d422016-06-15 11:41:42 -0700519 } else {
520 LOG(ERROR) << "Unknown sort key for slab allocation: " << key;
521 return false;
522 }
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200523 slab_sample_tree_builder_.reset(new SlabSampleTreeBuilder(comparator, &thread_tree_));
524 slab_sample_tree_builder_->SetCallChainSampleOptions(accumulate_callchain_, print_callgraph_,
525 !callgraph_show_callee_);
Yabin Cui6965d422016-06-15 11:41:42 -0700526 sort_comparator.AddComparator(comparator);
527 slab_sample_tree_sorter_.reset(new SlabSampleTreeSorter(sort_comparator));
528 slab_sample_tree_displayer_.reset(new SlabSampleTreeDisplayer(displayer));
529 }
530 }
531 return true;
532}
533
534void KmemCommand::ReadEventAttrsFromRecordFile() {
Yabin Cui06ed3ef2023-04-07 13:19:00 -0700535 for (const EventAttrWithId& attr_with_id : record_file_reader_->AttrSection()) {
Yabin Cui6965d422016-06-15 11:41:42 -0700536 EventAttrWithName attr;
Yabin Cui06ed3ef2023-04-07 13:19:00 -0700537 attr.attr = attr_with_id.attr;
Yabin Cui6965d422016-06-15 11:41:42 -0700538 attr.event_ids = attr_with_id.ids;
539 attr.name = GetEventNameByAttr(attr.attr);
540 event_attrs_.push_back(attr);
541 }
542}
543
544bool KmemCommand::ReadFeaturesFromRecordFile() {
Yabin Cui90a547e2022-12-07 16:29:13 -0800545 if (!record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_)) {
546 return false;
547 }
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200548 std::string arch = record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
Yabin Cui6965d422016-06-15 11:41:42 -0700549 if (!arch.empty()) {
550 record_file_arch_ = GetArchType(arch);
551 if (record_file_arch_ == ARCH_UNSUPPORTED) {
552 return false;
553 }
554 }
555 std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
556 if (!cmdline.empty()) {
557 record_cmdline_ = android::base::Join(cmdline, ' ');
558 }
559 if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_TRACING_DATA)) {
560 std::vector<char> tracing_data;
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200561 if (!record_file_reader_->ReadFeatureSection(PerfFileFormat::FEAT_TRACING_DATA,
562 &tracing_data)) {
Yabin Cui6965d422016-06-15 11:41:42 -0700563 return false;
564 }
565 ProcessTracingData(tracing_data);
566 }
567 return true;
568}
569
570bool KmemCommand::ReadSampleTreeFromRecordFile() {
571 if (!record_file_reader_->ReadDataSection(
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200572 [this](std::unique_ptr<Record> record) { return ProcessRecord(std::move(record)); })) {
Yabin Cui6965d422016-06-15 11:41:42 -0700573 return false;
574 }
575 if (use_slab_) {
576 slab_sample_tree_ = slab_sample_tree_builder_->GetSampleTree();
577 slab_sample_tree_sorter_->Sort(slab_sample_tree_.samples, print_callgraph_);
578 }
579 return true;
580}
581
582bool KmemCommand::ProcessRecord(std::unique_ptr<Record> record) {
583 thread_tree_.Update(*record);
584 if (record->type() == PERF_RECORD_SAMPLE) {
585 if (use_slab_) {
586 slab_sample_tree_builder_->ProcessSampleRecord(
587 *static_cast<const SampleRecord*>(record.get()));
588 }
Yabin Cui8cd92332018-03-21 14:34:29 -0700589 } else if (record->type() == PERF_RECORD_TRACING_DATA ||
590 record->type() == SIMPLE_PERF_RECORD_TRACING_DATA) {
Yabin Cui6965d422016-06-15 11:41:42 -0700591 const auto& r = *static_cast<TracingDataRecord*>(record.get());
Yabin Cui540932c2022-09-13 15:52:33 -0700592 if (!ProcessTracingData(std::vector<char>(r.data, r.data + r.data_size))) {
593 return false;
594 }
Yabin Cui6965d422016-06-15 11:41:42 -0700595 }
596 return true;
597}
598
Yabin Cui540932c2022-09-13 15:52:33 -0700599bool KmemCommand::ProcessTracingData(const std::vector<char>& data) {
600 auto tracing = Tracing::Create(data);
601 if (!tracing) {
602 return false;
603 }
Yabin Cui6965d422016-06-15 11:41:42 -0700604 for (auto& attr : event_attrs_) {
605 if (attr.attr.type == PERF_TYPE_TRACEPOINT) {
606 uint64_t trace_event_id = attr.attr.config;
Yabin Cui540932c2022-09-13 15:52:33 -0700607 attr.name = tracing->GetTracingEventNameHavingId(trace_event_id);
Yabin Cuic73d6332023-07-27 13:45:30 -0700608 std::optional<TracingFormat> opt_format = tracing->GetTracingFormatHavingId(trace_event_id);
609 if (!opt_format.has_value()) {
610 return false;
611 }
612 const TracingFormat& format = opt_format.value();
Yabin Cui6965d422016-06-15 11:41:42 -0700613 if (use_slab_) {
614 if (format.name == "kmalloc" || format.name == "kmem_cache_alloc" ||
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200615 format.name == "kmalloc_node" || format.name == "kmem_cache_alloc_node") {
Yabin Cui6965d422016-06-15 11:41:42 -0700616 SlabFormat f;
617 f.type = SlabFormat::KMEM_ALLOC;
618 format.GetField("call_site", f.call_site);
619 format.GetField("ptr", f.ptr);
620 format.GetField("bytes_req", f.bytes_req);
621 format.GetField("bytes_alloc", f.bytes_alloc);
622 format.GetField("gfp_flags", f.gfp_flags);
623 slab_sample_tree_builder_->AddSlabFormat(attr.event_ids, f);
624 } else if (format.name == "kfree" || format.name == "kmem_cache_free") {
625 SlabFormat f;
626 f.type = SlabFormat::KMEM_FREE;
627 format.GetField("call_site", f.call_site);
628 format.GetField("ptr", f.ptr);
629 slab_sample_tree_builder_->AddSlabFormat(attr.event_ids, f);
630 }
631 }
632 }
633 }
Yabin Cui540932c2022-09-13 15:52:33 -0700634 return true;
Yabin Cui6965d422016-06-15 11:41:42 -0700635}
636
637bool KmemCommand::PrintReport() {
638 std::unique_ptr<FILE, decltype(&fclose)> file_handler(nullptr, fclose);
639 FILE* report_fp = stdout;
640 if (!report_filename_.empty()) {
641 file_handler.reset(fopen(report_filename_.c_str(), "w"));
642 if (file_handler == nullptr) {
643 PLOG(ERROR) << "failed to open " << report_filename_;
644 return false;
645 }
646 report_fp = file_handler.get();
647 }
648 PrintReportContext(report_fp);
649 if (use_slab_) {
650 fprintf(report_fp, "\n\n");
651 PrintSlabReportContext(report_fp);
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200652 slab_sample_tree_displayer_->DisplaySamples(report_fp, slab_sample_tree_.samples,
653 &slab_sample_tree_);
Yabin Cui6965d422016-06-15 11:41:42 -0700654 }
655 return true;
656}
657
658void KmemCommand::PrintReportContext(FILE* fp) {
659 if (!record_cmdline_.empty()) {
660 fprintf(fp, "Cmdline: %s\n", record_cmdline_.c_str());
661 }
662 fprintf(fp, "Arch: %s\n", GetArchString(record_file_arch_).c_str());
663 for (const auto& attr : event_attrs_) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200664 fprintf(fp, "Event: %s (type %u, config %llu)\n", attr.name.c_str(), attr.attr.type,
665 attr.attr.config);
Yabin Cui6965d422016-06-15 11:41:42 -0700666 }
667}
668
669void KmemCommand::PrintSlabReportContext(FILE* fp) {
670 fprintf(fp, "Slab allocation information:\n");
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200671 fprintf(fp, "Total requested bytes: %" PRIu64 "\n", slab_sample_tree_.total_requested_bytes);
672 fprintf(fp, "Total allocated bytes: %" PRIu64 "\n", slab_sample_tree_.total_allocated_bytes);
673 uint64_t fragment =
674 slab_sample_tree_.total_allocated_bytes - slab_sample_tree_.total_requested_bytes;
Yabin Cui6965d422016-06-15 11:41:42 -0700675 double percentage = 0.0;
676 if (slab_sample_tree_.total_allocated_bytes != 0) {
677 percentage = 100.0 * fragment / slab_sample_tree_.total_allocated_bytes;
678 }
679 fprintf(fp, "Total fragment: %" PRIu64 ", %f%%\n", fragment, percentage);
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200680 fprintf(fp, "Total allocations: %" PRIu64 "\n", slab_sample_tree_.nr_allocations);
Yabin Cui6965d422016-06-15 11:41:42 -0700681 fprintf(fp, "Total frees: %" PRIu64 "\n", slab_sample_tree_.nr_frees);
682 percentage = 0.0;
683 if (slab_sample_tree_.nr_allocations != 0) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200684 percentage =
685 100.0 * slab_sample_tree_.nr_cross_cpu_allocations / slab_sample_tree_.nr_allocations;
Yabin Cui6965d422016-06-15 11:41:42 -0700686 }
687 fprintf(fp, "Total cross cpu allocation/free: %" PRIu64 ", %f%%\n",
688 slab_sample_tree_.nr_cross_cpu_allocations, percentage);
689 fprintf(fp, "\n");
690}
691
692} // namespace
693
694void RegisterKmemCommand() {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200695 RegisterCommand("kmem", [] { return std::unique_ptr<Command>(new KmemCommand()); });
Yabin Cui6965d422016-06-15 11:41:42 -0700696}
Yabin Cuiacbdb242020-07-07 15:56:34 -0700697
698} // namespace simpleperf