blob: ce938eb9abbafb5c1e3027d9c6f5b9ac029c2dd7 [file] [log] [blame]
Christopher Ferris2c2d5742025-09-04 20:05:11 +00001/*
2 * Copyright (C) 2025 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 <errno.h>
18#include <string.h>
19#include <sys/stat.h>
20
21#include <iostream>
22#include <memory>
23#include <string_view>
24
25#include <memory_trace/TraceInfo.h>
26
27#include "OpGenAnalysis.h"
28#include "OpOverlapAnalysis.h"
29
30namespace {
31
32class OptionBase;
33
34class OptionRegistry {
35 public:
36 static OptionRegistry& get() {
37 static OptionRegistry registry;
38 return registry;
39 }
40
41 void RegisterOption(OptionBase* opt) { options_.push_back(opt); }
42
43 auto begin() { return options_.begin(); }
44 auto end() { return options_.end(); }
45
46 void Stream(std::ostream& os, std::string_view indent = "\t");
47
48 private:
49 OptionRegistry() {}
50
51 std::vector<OptionBase*> options_;
52};
53
54class OptionBase {
55 public:
56 template <typename... Analyses>
57 OptionBase(std::string_view name, std::string_view desc) : name_(name), desc_(desc) {
58 OptionRegistry::get().RegisterOption(this);
59 }
60
61 std::string_view name() const { return name_; }
62 std::string_view desc() const { return desc_; }
63
64 void GetAnalyses(std::vector<Analysis*>& analyses) {
65 for (auto& analysis : analyses_) analyses.push_back(analysis.get());
66 }
67
68 protected:
69 template <typename Analysis, typename... Analyses,
70 std::enable_if_t<(sizeof...(Analyses) > 0), bool> = true>
71 void AppendAnalyses() {
72 AppendAnalyses<Analysis>();
73 AppendAnalyses<Analyses...>();
74 }
75 template <typename Analysis>
76 void AppendAnalyses() {
77 analyses_.push_back(std::move(std::make_unique<Analysis>()));
78 }
79
80 std::string_view name_;
81 std::string_view desc_;
82 std::vector<std::unique_ptr<Analysis>> analyses_;
83};
84
85template <typename... Analyses>
86class Option : public OptionBase {
87 public:
88 Option(std::string_view name, std::string_view desc) : OptionBase(name, desc) {
89 AppendAnalyses<Analyses...>();
90 }
91};
92
93} // namespace
94
95void OptionRegistry::Stream(std::ostream& os, std::string_view indent) {
96 for (OptionBase* opt : options_) {
97 os << indent << "--" << opt->name() << ": " << opt->desc() << std::endl;
98 }
99}
100
101static bool ParseArgs(int argc, char** argv, std::vector<Analysis*>& analyses,
102 std::vector<std::string>& traces) {
103 auto& registry = OptionRegistry::get();
104 for (int i = 0; i < argc; ++i) {
105 if (argv[i][0] != '-') {
106 traces.push_back(argv[i]);
107 struct stat st;
108 if (stat(traces.back().c_str(), &st) != 0) {
109 if (errno == ENOENT) {
110 std::cerr << traces.back() << " does not exist." << std::endl;
111 } else {
112 std::cerr << "stat of file " << traces.back() << " failed: " << strerror(errno)
113 << std::endl;
114 }
115 return false;
116 }
117 if (!S_ISREG(st.st_mode)) {
118 std::cerr << traces.back() << " is not a regular file." << std::endl;
119 return false;
120 }
121 continue;
122 } else if (argv[i][1] != '-') {
123 std::cerr << "Unknown option: " << argv[i] << std::endl;
124 return false;
125 }
126
127 bool found = false;
128 std::string_view arg(&argv[i][2]);
129 for (auto* option : registry) {
130 if (option->name() == arg) {
131 option->GetAnalyses(analyses);
132 found = true;
133 break;
134 }
135 }
136 if (!found) {
137 std::cerr << "Unknown option: " << argv[i] << std::endl;
138 return false;
139 }
140 }
141 return true;
142}
143
144void Usage() {
145 std::cerr << "Usage: memory_trace_analysis TRACE_FILE1 TRACE_FILE2 ... [Analyses...]\n";
146 std::cerr << "Analyses:\n";
147 OptionRegistry::get().Stream(std::cerr);
148}
149
150int main(int argc, char** argv) {
151 static Option<OpMinMaxAnalysis, OpAverageAnalysis> gen(
152 "op-gen-stats", "get the min/max/avg of each kind of alloc operation");
153 static Option<OpOverlapAnalysis> overlap(
154 "op-overlap", "get the amount of overlap in between each kind of alloc operation");
155
156 if (argc < 2) {
157 Usage();
158 return 1;
159 }
160
161 std::vector<Analysis*> analyses;
162 std::vector<std::string> traces;
163 if (!ParseArgs(argc - 1, argv + 1, analyses, traces)) {
164 Usage();
165 return 1;
166 }
167 if (analyses.empty()) {
168 std::cerr << "No analyses chosen.\n";
169 Usage();
170 return 1;
171 }
172 if (traces.empty()) {
173 std::cerr << "No trace files.\n";
174 Usage();
175 return 1;
176 }
177
178 for (auto& trace : traces) {
179 std::cout << "Analyzing trace " << trace << std::endl;
180 memory_trace::TraceInfo info;
181 info.Init(trace.c_str());
182 for (auto& analysis : analyses) {
183 std::cout << " " << analysis->Name() << std::endl;
184 analysis->Gather(info);
185 analysis->StreamResult(std::cout, " ");
186 }
187 }
188 return 0;
189}