1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <getopt.h>
18 #include <inttypes.h>
19 #include <stdint.h>
20 #include <unistd.h>
21
22 #include <thread>
23
24 #include "perfetto/base/logging.h"
25 #include "perfetto/base/time.h"
26
27 #define PERFETTO_HAVE_PTHREADS \
28 (PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
29 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
30 PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX))
31
32 #if PERFETTO_HAVE_PTHREADS
33 #include <pthread.h>
34 #endif
35
36 // Spawns the requested number threads that alternate between busy-waiting and
37 // sleeping.
38
39 namespace perfetto {
40 namespace {
41
SetRandomThreadName(uint32_t thread_name_count)42 void SetRandomThreadName(uint32_t thread_name_count) {
43 #if PERFETTO_HAVE_PTHREADS
44 char name[16] = {};
45 snprintf(name, sizeof(name), "busy-%" PRIu32,
46 static_cast<uint32_t>(rand()) % thread_name_count);
47 pthread_setname_np(pthread_self(), name);
48 #endif
49 }
50
PrintUsage(const char * bin_name)51 void PrintUsage(const char* bin_name) {
52 #if PERFETTO_HAVE_PTHREADS
53 PERFETTO_ELOG(
54 "Usage: %s --threads=N --period_us=N --duty_cycle=[1-100] "
55 "[--thread_names=N]",
56 bin_name);
57 #else
58 PERFETTO_ELOG("Usage: %s --threads=N --period_us=N --duty_cycle=[1-100]",
59 bin_name);
60 #endif
61 }
62
BusyWait(int64_t tstart,int64_t period_us,int64_t busy_us,uint32_t thread_name_count)63 __attribute__((noreturn)) void BusyWait(int64_t tstart,
64 int64_t period_us,
65 int64_t busy_us,
66 uint32_t thread_name_count) {
67 int64_t tbusy = tstart;
68 int64_t tnext = tstart;
69 for (;;) {
70 if (thread_name_count)
71 SetRandomThreadName(thread_name_count);
72
73 tbusy = tnext + busy_us * 1000;
74 tnext += period_us * 1000;
75 while (base::GetWallTimeNs().count() < tbusy) {
76 for (int i = 0; i < 10000; i++) {
77 asm volatile("" ::: "memory");
78 }
79 }
80 auto tnow = base::GetWallTimeNs().count();
81 if (tnow >= tnext) {
82 std::this_thread::yield();
83 continue;
84 }
85
86 while (tnow < tnext) {
87 // +1 to prevent sleeping twice when there is truncation.
88 base::SleepMicroseconds(static_cast<uint32_t>((tnext - tnow) / 1000) + 1);
89 tnow = base::GetWallTimeNs().count();
90 }
91 }
92 }
93
BusyThreadsMain(int argc,char ** argv)94 int BusyThreadsMain(int argc, char** argv) {
95 int64_t num_threads = -1;
96 int64_t period_us = -1;
97 int64_t duty_cycle = -1;
98 uint32_t thread_name_count = 0;
99
100 static struct option long_options[] = {
101 {"threads", required_argument, nullptr, 't'},
102 {"period_us", required_argument, nullptr, 'p'},
103 {"duty_cycle", required_argument, nullptr, 'd'},
104 #if PERFETTO_HAVE_PTHREADS
105 {"thread_names", required_argument, nullptr, 'r'},
106 #endif
107 {nullptr, 0, nullptr, 0}
108 };
109 int option_index;
110 int c;
111 while ((c = getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
112 switch (c) {
113 case 't':
114 num_threads = atol(optarg);
115 break;
116 case 'p':
117 period_us = atol(optarg);
118 break;
119 case 'd':
120 duty_cycle = atol(optarg);
121 break;
122 #if PERFETTO_HAVE_PTHREADS
123 case 'r':
124 thread_name_count = static_cast<uint32_t>(atoi(optarg));
125 break;
126 #endif
127 default:
128 break;
129 }
130 }
131 if (num_threads < 1 || period_us < 0 || duty_cycle < 1 || duty_cycle > 100 ||
132 thread_name_count > (1 << 20)) {
133 PrintUsage(argv[0]);
134 return 1;
135 }
136
137 int64_t busy_us = static_cast<int64_t>(period_us * (duty_cycle / 100.0));
138
139 PERFETTO_LOG("Spawning %" PRId64 " threads; period duration: %" PRId64
140 "us; busy duration: %" PRId64 "us.",
141 num_threads, period_us, busy_us);
142
143 int64_t tstart = base::GetWallTimeNs().count();
144 for (int i = 0; i < num_threads; i++) {
145 std::thread th(BusyWait, tstart, period_us, busy_us, thread_name_count);
146 th.detach();
147 }
148 PERFETTO_LOG("Threads spawned, Ctrl-C to stop.");
149 while (sleep(600))
150 ;
151
152 return 0;
153 }
154
155 } // namespace
156 } // namespace perfetto
157
main(int argc,char ** argv)158 int main(int argc, char** argv) {
159 return perfetto::BusyThreadsMain(argc, argv);
160 }
161