| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Tool that takes in a stream of allocations from stdin and outputs samples |
| // |
| // Input format is code_location size tuples, output format is iteration number |
| // code_location sample_size tuples. The sum of all allocations in the input is |
| // echoed back in the special iteration 'g' |
| // |
| // Example input: |
| // foo 1 |
| // bar 10 |
| // foo 1000 |
| // baz 1 |
| // |
| // Example output; |
| // g foo 1001 |
| // g bar 10 |
| // g baz 1 |
| // 1 foo 1000 |
| // 1 bar 100 |
| |
| #include <iostream> |
| #include <string> |
| #include <thread> |
| |
| #include <unistd.h> |
| |
| #include "src/profiling/memory/client.h" |
| #include "src/profiling/memory/sampler.h" |
| |
| #include "perfetto/base/logging.h" |
| |
| namespace perfetto { |
| namespace profiling { |
| namespace { |
| |
| constexpr uint64_t kDefaultSamplingInterval = 128000; |
| |
| int ProfilingSampleDistributionMain(int argc, char** argv) { |
| int opt; |
| uint64_t sampling_interval = kDefaultSamplingInterval; |
| uint64_t times = 1; |
| uint64_t init_seed = 1; |
| |
| while ((opt = getopt(argc, argv, "t:i:s:")) != -1) { |
| switch (opt) { |
| case 'i': { |
| char* end; |
| long long sampling_interval_arg = strtoll(optarg, &end, 10); |
| if (*end != '\0' || *optarg == '\0') |
| PERFETTO_FATAL("Invalid sampling interval: %s", optarg); |
| PERFETTO_CHECK(sampling_interval_arg > 0); |
| sampling_interval = static_cast<uint64_t>(sampling_interval_arg); |
| break; |
| } |
| case 't': { |
| char* end; |
| long long times_arg = strtoll(optarg, &end, 10); |
| if (*end != '\0' || *optarg == '\0') |
| PERFETTO_FATAL("Invalid times: %s", optarg); |
| PERFETTO_CHECK(times_arg > 0); |
| times = static_cast<uint64_t>(times_arg); |
| break; |
| } |
| case 's': { |
| char* end; |
| init_seed = static_cast<uint64_t>(strtoll(optarg, &end, 10)); |
| if (*end != '\0' || *optarg == '\0') |
| PERFETTO_FATAL("Invalid seed: %s", optarg); |
| break; |
| } |
| |
| default: |
| PERFETTO_FATAL("%s [-t times] [-i interval] [-s seed]", argv[0]); |
| } |
| } |
| |
| std::vector<std::pair<std::string, uint64_t>> allocations; |
| |
| while (std::cin) { |
| std::string callsite; |
| uint64_t size; |
| std::cin >> callsite; |
| if (std::cin.fail()) { |
| // Skip trailing newline. |
| if (std::cin.eof()) |
| break; |
| PERFETTO_FATAL("Could not read callsite"); |
| } |
| std::cin >> size; |
| if (std::cin.fail()) |
| PERFETTO_FATAL("Could not read size"); |
| allocations.emplace_back(std::move(callsite), size); |
| } |
| std::map<std::string, uint64_t> total_ground_truth; |
| for (const auto& pair : allocations) |
| total_ground_truth[pair.first] += pair.second; |
| |
| for (const auto& pair : total_ground_truth) |
| std::cout << "g " << pair.first << " " << pair.second << std::endl; |
| |
| std::default_random_engine seed_engine(init_seed); |
| |
| while (times-- > 0) { |
| PThreadKey key(ThreadLocalSamplingData::KeyDestructor); |
| ThreadLocalSamplingData::seed = seed_engine(); |
| // We want to use the same API here that the client uses, which involves |
| // TLS. In order to destruct that TLS, we need to spawn a thread because |
| // pthread_key_delete does not delete any associated data, but rather it |
| // gets deleted when the owning thread terminates. |
| // |
| // Sad times. |
| std::thread th([&] { |
| if (!key.valid()) |
| PERFETTO_FATAL("Failed to initialize TLS."); |
| |
| std::map<std::string, uint64_t> totals; |
| for (const auto& pair : allocations) { |
| size_t sample_size = |
| SampleSize(key.get(), pair.second, sampling_interval, malloc, free); |
| // We also want to add 0 to make downstream processing easier, making |
| // sure every iteration has an entry for every key, even if it is |
| // zero. |
| totals[pair.first] += sample_size; |
| } |
| |
| for (const auto& pair : totals) |
| std::cout << times << " " << pair.first << " " << pair.second |
| << std::endl; |
| }); |
| th.join(); |
| } |
| |
| return 0; |
| } |
| |
| } // namespace |
| } // namespace profiling |
| } // namespace perfetto |
| |
| int main(int argc, char** argv) { |
| return perfetto::profiling::ProfilingSampleDistributionMain(argc, argv); |
| } |