huangcm
2025-08-25 f350412dc55c15118d0a7925d1071877498e5e24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * 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);
}