blob: d6dc6d74935ce17c2a4953eaa36fd53236053da3 [file] [log] [blame]
/*
* Copyright (C) 2019 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 <getopt.h>
#include <inttypes.h>
#include <stdint.h>
#include <unistd.h>
#include <thread>
#include "perfetto/base/logging.h"
#include "perfetto/base/time.h"
#define PERFETTO_HAVE_PTHREADS \
(PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX))
#if PERFETTO_HAVE_PTHREADS
#include <pthread.h>
#endif
// Spawns the requested number threads that alternate between busy-waiting and
// sleeping.
namespace perfetto {
namespace {
void SetRandomThreadName(uint32_t thread_name_count) {
#if PERFETTO_HAVE_PTHREADS
char name[16] = {};
snprintf(name, sizeof(name), "busy-%" PRIu32,
static_cast<uint32_t>(rand()) % thread_name_count);
pthread_setname_np(pthread_self(), name);
#endif
}
void PrintUsage(const char* bin_name) {
#if PERFETTO_HAVE_PTHREADS
PERFETTO_ELOG(
"Usage: %s --threads=N --period_us=N --duty_cycle=[1-100] "
"[--thread_names=N]",
bin_name);
#else
PERFETTO_ELOG("Usage: %s --threads=N --period_us=N --duty_cycle=[1-100]",
bin_name);
#endif
}
__attribute__((noreturn)) void BusyWait(int64_t tstart,
int64_t period_us,
int64_t busy_us,
uint32_t thread_name_count) {
int64_t tbusy = tstart;
int64_t tnext = tstart;
for (;;) {
if (thread_name_count)
SetRandomThreadName(thread_name_count);
tbusy = tnext + busy_us * 1000;
tnext += period_us * 1000;
while (base::GetWallTimeNs().count() < tbusy) {
for (int i = 0; i < 10000; i++) {
asm volatile("" ::: "memory");
}
}
auto tnow = base::GetWallTimeNs().count();
if (tnow >= tnext) {
std::this_thread::yield();
continue;
}
while (tnow < tnext) {
// +1 to prevent sleeping twice when there is truncation.
base::SleepMicroseconds(static_cast<uint32_t>((tnext - tnow) / 1000) + 1);
tnow = base::GetWallTimeNs().count();
}
}
}
int BusyThreadsMain(int argc, char** argv) {
int64_t num_threads = -1;
int64_t period_us = -1;
int64_t duty_cycle = -1;
uint32_t thread_name_count = 0;
static struct option long_options[] = {
{"threads", required_argument, nullptr, 't'},
{"period_us", required_argument, nullptr, 'p'},
{"duty_cycle", required_argument, nullptr, 'd'},
#if PERFETTO_HAVE_PTHREADS
{"thread_names", required_argument, nullptr, 'r'},
#endif
{nullptr, 0, nullptr, 0}
};
int option_index;
int c;
while ((c = getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
switch (c) {
case 't':
num_threads = atol(optarg);
break;
case 'p':
period_us = atol(optarg);
break;
case 'd':
duty_cycle = atol(optarg);
break;
#if PERFETTO_HAVE_PTHREADS
case 'r':
thread_name_count = static_cast<uint32_t>(atoi(optarg));
break;
#endif
default:
break;
}
}
if (num_threads < 1 || period_us < 0 || duty_cycle < 1 || duty_cycle > 100 ||
thread_name_count > (1 << 20)) {
PrintUsage(argv[0]);
return 1;
}
int64_t busy_us = static_cast<int64_t>(period_us * (duty_cycle / 100.0));
PERFETTO_LOG("Spawning %" PRId64 " threads; period duration: %" PRId64
"us; busy duration: %" PRId64 "us.",
num_threads, period_us, busy_us);
int64_t tstart = base::GetWallTimeNs().count();
for (int i = 0; i < num_threads; i++) {
std::thread th(BusyWait, tstart, period_us, busy_us, thread_name_count);
th.detach();
}
PERFETTO_LOG("Threads spawned, Ctrl-C to stop.");
while (sleep(600))
;
return 0;
}
} // namespace
} // namespace perfetto
int main(int argc, char** argv) {
return perfetto::BusyThreadsMain(argc, argv);
}