|  | /* | 
|  | * 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); | 
|  | } |