• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 <err.h>
18 #include <errno.h>
19 #include <getopt.h>
20 #include <stdint.h>
21 #include <stdio.h>
22 
23 #include <limits>
24 #include <string_view>
25 #include <unordered_map>
26 
27 #include <android-base/file.h>
28 #include <android-base/parseint.h>
29 #include <android-base/strings.h>
30 
31 #include <memory_trace/MemoryTrace.h>
32 
33 #include "File.h"
34 
GetBaseExec()35 static std::string GetBaseExec() {
36   return android::base::Basename(android::base::GetExecutablePath());
37 }
38 
Usage()39 static void Usage() {
40   fprintf(
41       stderr,
42       "Usage: %s [--min_size SIZE] [--max_size SIZE] [--print_trace_format] [--help] TRACE_FILE\n",
43       GetBaseExec().c_str());
44   fprintf(stderr, "  --min_size SIZE\n");
45   fprintf(stderr, "      Display all allocations that are greater than or equal to SIZE\n");
46   fprintf(stderr, "  --max_size SIZE\n");
47   fprintf(stderr, "      Display all allocations that are less than or equal to SIZE\n");
48   fprintf(stderr, "  --print_trace_format\n");
49   fprintf(stderr, "      Display all allocations from the trace in the trace format\n");
50   fprintf(stderr, "  --help\n");
51   fprintf(stderr, "      Display this usage message\n");
52   fprintf(stderr, "  TRACE_FILE\n");
53   fprintf(stderr, "      The name of the trace file to filter\n");
54   fprintf(stderr, "\n  Display all of the allocations from the trace file that meet the filter\n");
55   fprintf(stderr, "  criteria. By default, without changing the min size or max size, all\n");
56   fprintf(stderr, "  allocations in the trace will be printed.\n");
57 }
58 
ParseOptions(int argc,char ** argv,size_t & min_size,size_t & max_size,bool & print_trace_format,std::string_view & trace_file)59 static bool ParseOptions(int argc, char** argv, size_t& min_size, size_t& max_size,
60                          bool& print_trace_format, std::string_view& trace_file) {
61   while (true) {
62     option options[] = {
63         {"min_size", required_argument, nullptr, 'i'},
64         {"max_size", required_argument, nullptr, 'x'},
65         {"print_trace_format", no_argument, nullptr, 'p'},
66         {"help", no_argument, nullptr, 'h'},
67         {nullptr, 0, nullptr, 0},
68     };
69     int option_index = 0;
70     int opt = getopt_long(argc, argv, "", options, &option_index);
71     if (opt == -1) {
72       break;
73     }
74 
75     switch (opt) {
76       case 'i':
77       case 'x':
78         size_t value;
79         if (!android::base::ParseUint<size_t>(optarg, &value)) {
80           fprintf(stderr, "%s: option '--%s' is not valid: %s\n", GetBaseExec().c_str(),
81                   options[option_index].name, optarg);
82           return false;
83         }
84         if (opt == 'i') {
85           min_size = value;
86         } else {
87           max_size = value;
88         }
89         break;
90       case 'p':
91         print_trace_format = true;
92         break;
93       case 'h':
94       default:
95         return false;
96     }
97   }
98   if (optind + 1 != argc) {
99     fprintf(stderr, "%s: only allows one argument.\n", GetBaseExec().c_str());
100     return false;
101   }
102   if (min_size > max_size) {
103     fprintf(stderr, "%s: min size(%zu) must be less than max size(%zu)\n", GetBaseExec().c_str(),
104             min_size, max_size);
105     return false;
106   }
107 
108   trace_file = argv[optind];
109   return true;
110 }
111 
PrintEntry(const memory_trace::Entry & entry,size_t size,bool print_trace_format)112 static void PrintEntry(const memory_trace::Entry& entry, size_t size, bool print_trace_format) {
113   if (print_trace_format) {
114     printf("%s\n", memory_trace::CreateStringFromEntry(entry).c_str());
115   } else {
116     printf("%s size %zu\n",
117            entry.type == memory_trace::REALLOC && entry.u.old_ptr != 0 ? "realloc" : "alloc", size);
118   }
119 }
120 
ProcessTrace(const std::string_view & trace,size_t min_size,size_t max_size,bool print_trace_format)121 static void ProcessTrace(const std::string_view& trace, size_t min_size, size_t max_size,
122                          bool print_trace_format) {
123   memory_trace::Entry* entries;
124   size_t num_entries;
125   GetUnwindInfo(trace.data(), &entries, &num_entries);
126 
127   if (!print_trace_format) {
128     if (max_size != std::numeric_limits<size_t>::max()) {
129       printf("Scanning for allocations between %zu and %zu\n", min_size, max_size);
130     } else if (min_size != 0) {
131       printf("Scanning for allocations >= %zu\n", min_size);
132     } else {
133       printf("Scanning for all allocations\n");
134     }
135   }
136   size_t total_allocs = 0;
137   size_t total_reallocs = 0;
138   for (size_t i = 0; i < num_entries; i++) {
139     const memory_trace::Entry& entry = entries[i];
140     switch (entry.type) {
141       case memory_trace::MALLOC:
142       case memory_trace::MEMALIGN:
143       case memory_trace::REALLOC:
144         if (entry.size >= min_size && entry.size <= max_size) {
145           PrintEntry(entry, entry.size, print_trace_format);
146           if (entry.type == memory_trace::REALLOC) {
147             total_reallocs++;
148           } else {
149             total_allocs++;
150           }
151         }
152         break;
153 
154       case memory_trace::CALLOC:
155         if (size_t size = entry.u.n_elements * entry.size;
156             size >= min_size && entry.size <= max_size) {
157           PrintEntry(entry, size, print_trace_format);
158         }
159         break;
160 
161       case memory_trace::FREE:
162       case memory_trace::THREAD_DONE:
163       default:
164         break;
165     }
166   }
167   if (!print_trace_format) {
168     printf("Total allocs:   %zu\n", total_allocs);
169     printf("Total reallocs: %zu\n", total_reallocs);
170   }
171 
172   FreeEntries(entries, num_entries);
173 }
174 
main(int argc,char ** argv)175 int main(int argc, char** argv) {
176   size_t min_size = 0;
177   size_t max_size = std::numeric_limits<size_t>::max();
178   bool print_trace_format = false;
179   std::string_view trace_file;
180   if (!ParseOptions(argc, argv, min_size, max_size, print_trace_format, trace_file)) {
181     Usage();
182     return 1;
183   }
184 
185   ProcessTrace(trace_file, min_size, max_size, print_trace_format);
186   return 0;
187 }
188