blob: 5bc7a575b89ab384ad7e6e40b6e232f360754f5f [file] [log] [blame]
Florian Mayerbb54f5e2018-10-22 12:13:03 +01001/*
2 * Copyright (C) 2018 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// Tool that takes in a stream of allocations from stdin and outputs samples
18//
19// Input format is code_location size tuples, output format is iteration number
20// code_location sample_size tuples. The sum of all allocations in the input is
21// echoed back in the special iteration 'g'
22//
23// Example input:
24// foo 1
25// bar 10
26// foo 1000
27// baz 1
28//
29// Example output;
30// g foo 1001
31// g bar 10
32// g baz 1
33// 1 foo 1000
34// 1 bar 100
35
36#include <iostream>
37#include <string>
38#include <thread>
39
40#include <unistd.h>
41
42#include "src/profiling/memory/client.h"
43#include "src/profiling/memory/sampler.h"
44
45#include "perfetto/base/logging.h"
46
47namespace perfetto {
48namespace {
49
50constexpr uint64_t kDefaultSamplingRate = 128000;
51
52int ProfilingSampleDistributionMain(int argc, char** argv) {
53 int opt;
54 uint64_t sampling_rate = kDefaultSamplingRate;
55 uint64_t times = 1;
56 uint64_t init_seed = 1;
57
58 while ((opt = getopt(argc, argv, "t:r:s:")) != -1) {
59 switch (opt) {
60 case 'r': {
61 char* end;
62 long long sampling_rate_arg = strtoll(optarg, &end, 10);
63 if (*end != '\0' || *optarg == '\0')
64 PERFETTO_FATAL("Invalid sampling rate: %s", optarg);
65 PERFETTO_CHECK(sampling_rate > 0);
66 sampling_rate = static_cast<uint64_t>(sampling_rate_arg);
67 break;
68 }
69 case 't': {
70 char* end;
71 long long times_arg = strtoll(optarg, &end, 10);
72 if (*end != '\0' || *optarg == '\0')
73 PERFETTO_FATAL("Invalid times: %s", optarg);
74 PERFETTO_CHECK(times_arg > 0);
75 times = static_cast<uint64_t>(times_arg);
76 break;
77 }
78 case 's': {
79 char* end;
80 init_seed = static_cast<uint64_t>(strtoll(optarg, &end, 10));
81 if (*end != '\0' || *optarg == '\0')
82 PERFETTO_FATAL("Invalid seed: %s", optarg);
83 break;
84 }
85
86 default:
87 PERFETTO_FATAL("%s [-t times] [-r rate] [-s seed]", argv[0]);
88 }
89 }
90
91 std::vector<std::pair<std::string, uint64_t>> allocations;
92
93 while (std::cin) {
94 std::string callsite;
95 uint64_t size;
96 std::cin >> callsite;
97 if (std::cin.fail()) {
98 // Skip trailing newline.
99 if (std::cin.eof())
100 break;
101 PERFETTO_FATAL("Could not read callsite");
102 }
103 std::cin >> size;
104 if (std::cin.fail())
105 PERFETTO_FATAL("Could not read size");
106 allocations.emplace_back(std::move(callsite), size);
107 }
108 std::map<std::string, uint64_t> total_ground_truth;
109 for (const auto& pair : allocations)
110 total_ground_truth[pair.first] += pair.second;
111
112 for (const auto& pair : total_ground_truth)
113 std::cout << "g " << pair.first << " " << pair.second << std::endl;
114
115 std::default_random_engine seed_engine(init_seed);
116
117 while (times-- > 0) {
118 PThreadKey key(ThreadLocalSamplingData::KeyDestructor);
119 ThreadLocalSamplingData::seed = seed_engine();
120 // We want to use the same API here that the client uses, which involves
121 // TLS. In order to destruct that TLS, we need to spawn a thread because
122 // pthread_key_delete does not delete any associated data, but rather it
123 // gets deleted when the owning thread terminates.
124 //
125 // Sad times.
126 std::thread th([&] {
127 if (!key.valid())
128 PERFETTO_FATAL("Failed to initialize TLS.");
129
130 std::map<std::string, uint64_t> totals;
131 for (const auto& pair : allocations) {
132 size_t sample_size =
133 SampleSize(key.get(), pair.second, sampling_rate, malloc, free);
134 // We also want to add 0 to make downstream processing easier, making
135 // sure every iteration has an entry for every key, even if it is
136 // zero.
137 totals[pair.first] += sample_size;
138 }
139
140 for (const auto& pair : totals)
141 std::cout << times << " " << pair.first << " " << pair.second
142 << std::endl;
143 });
144 th.join();
145 }
146
147 return 0;
148}
149
150} // namespace
151} // namespace perfetto
152
153int main(int argc, char** argv) {
154 return perfetto::ProfilingSampleDistributionMain(argc, argv);
155}