/* * Copyright (C) 2020 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. */ #include #include #include #include #include #include #include #include #include #include #include "perfetto/base/logging.h" #include "perfetto/base/time.h" #include "perfetto/ext/base/getopt.h" #include "perfetto/ext/base/optional.h" #include "perfetto/ext/base/string_utils.h" #include "perfetto/heap_profile.h" namespace { void EnabledCallback(void*, const AHeapProfileEnableCallbackInfo*); std::atomic done; std::atomic allocs{0}; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wglobal-constructors" #pragma GCC diagnostic ignored "-Wexit-time-destructors" std::mutex g_wake_up_mutex; std::condition_variable g_wake_up_cv; uint64_t g_rate = 0; uint32_t g_heap_id = AHeapProfile_registerHeap( AHeapInfo_setEnabledCallback(AHeapInfo_create("test_heap"), EnabledCallback, nullptr)); #pragma GCC diagnostic pop void EnabledCallback(void*, const AHeapProfileEnableCallbackInfo* info) { std::lock_guard l(g_wake_up_mutex); g_rate = AHeapProfileEnableCallbackInfo_getSamplingInterval(info); g_wake_up_cv.notify_all(); } uint64_t ScrambleAllocId(uint64_t alloc_id, uint32_t thread_idx) { return thread_idx | (~alloc_id << 24); } void Thread(uint32_t thread_idx, uint64_t pending_allocs) { PERFETTO_CHECK(thread_idx < 1 << 24); uint64_t alloc_id = 0; size_t thread_allocs = 0; while (!done.load(std::memory_order_relaxed)) { AHeapProfile_reportAllocation(g_heap_id, ScrambleAllocId(alloc_id, thread_idx), 1); if (alloc_id > pending_allocs) AHeapProfile_reportFree( g_heap_id, ScrambleAllocId(alloc_id - pending_allocs, thread_idx)); alloc_id++; thread_allocs++; } allocs.fetch_add(thread_allocs, std::memory_order_relaxed); } } // namespace int main(int argc, char** argv) { if (argc != 4) { PERFETTO_FATAL("%s NUMBER_THREADS RUNTIME_MS PENDING_ALLOCS", argv[0]); } perfetto::base::Optional opt_no_threads = perfetto::base::CStringToUInt64(argv[1]); if (!opt_no_threads) { PERFETTO_FATAL("Invalid number of threads: %s", argv[1]); } uint64_t no_threads = *opt_no_threads; perfetto::base::Optional opt_runtime_ms = perfetto::base::CStringToUInt64(argv[2]); if (!opt_runtime_ms) { PERFETTO_FATAL("Invalid runtime: %s", argv[2]); } uint64_t runtime_ms = *opt_runtime_ms; perfetto::base::Optional opt_pending_allocs = perfetto::base::CStringToUInt64(argv[3]); if (!opt_runtime_ms) { PERFETTO_FATAL("Invalid number of pending allocs: %s", argv[3]); } uint64_t pending_allocs = *opt_pending_allocs; std::unique_lock l(g_wake_up_mutex); g_wake_up_cv.wait(l, [] { return g_rate > 0; }); perfetto::base::TimeMillis end = perfetto::base::GetWallTimeMs() + perfetto::base::TimeMillis(runtime_ms); std::vector threads; for (size_t i = 0; i < static_cast(no_threads); ++i) threads.emplace_back(Thread, i, pending_allocs); perfetto::base::TimeMillis current = perfetto::base::GetWallTimeMs(); while (current < end) { usleep(useconds_t((end - current).count()) * 1000); current = perfetto::base::GetWallTimeMs(); } done.store(true, std::memory_order_relaxed); for (std::thread& th : threads) th.join(); printf("%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 "\n", no_threads, runtime_ms, pending_allocs, g_rate, allocs.load(std::memory_order_relaxed)); return 0; }