blob: 8a2195af851a2842ca0cf2217388ccbeafe02466 [file] [log] [blame]
/*
* 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);
}