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 <inttypes.h>
18 #include <stdint.h>
19 #include <unistd.h>
20
21 #include <thread>
22
23 #include "perfetto/base/logging.h"
24 #include "perfetto/base/time.h"
25 #include "perfetto/ext/base/file_utils.h"
26 #include "perfetto/ext/base/getopt.h"
27 #include "perfetto/ext/base/scoped_file.h"
28
29 #define PERFETTO_HAVE_PTHREADS \
30 (PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
31 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
32 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE))
33
34 #if PERFETTO_HAVE_PTHREADS
35 #include <pthread.h>
36 #endif
37
38 // Spawns the requested number threads that alternate between busy-waiting and
39 // sleeping.
40
41 namespace perfetto {
42 namespace {
43
SetRandomThreadName(uint32_t thread_name_count)44 void SetRandomThreadName(uint32_t thread_name_count) {
45 #if PERFETTO_HAVE_PTHREADS
46 char name[16] = {};
47 snprintf(name, sizeof(name), "busy-%" PRIu32,
48 static_cast<uint32_t>(rand()) % thread_name_count);
49 pthread_setname_np(pthread_self(), name);
50 #endif
51 }
52
PrintUsage(const char * bin_name)53 void PrintUsage(const char* bin_name) {
54 #if PERFETTO_HAVE_PTHREADS
55 PERFETTO_ELOG(
56 "Usage: %s [--background] --threads=N --period_us=N --duty_cycle=[1-100] "
57 "[--thread_names=N]",
58 bin_name);
59 #else
60 PERFETTO_ELOG(
61 "Usage: %s [--background] --threads=N --period_us=N --duty_cycle=[1-100]",
62 bin_name);
63 #endif
64 }
65
BusyWait(int64_t tstart,int64_t period_us,int64_t busy_us,uint32_t thread_name_count)66 __attribute__((noreturn)) void BusyWait(int64_t tstart,
67 int64_t period_us,
68 int64_t busy_us,
69 uint32_t thread_name_count) {
70 int64_t tbusy = tstart;
71 int64_t tnext = tstart;
72 for (;;) {
73 if (thread_name_count)
74 SetRandomThreadName(thread_name_count);
75
76 tbusy = tnext + busy_us * 1000;
77 tnext += period_us * 1000;
78 while (base::GetWallTimeNs().count() < tbusy) {
79 for (int i = 0; i < 10000; i++) {
80 asm volatile("" ::: "memory");
81 }
82 }
83 auto tnow = base::GetWallTimeNs().count();
84 if (tnow >= tnext) {
85 std::this_thread::yield();
86 continue;
87 }
88
89 while (tnow < tnext) {
90 // +1 to prevent sleeping twice when there is truncation.
91 base::SleepMicroseconds(static_cast<uint32_t>((tnext - tnow) / 1000) + 1);
92 tnow = base::GetWallTimeNs().count();
93 }
94 }
95 }
96
BusyThreadsMain(int argc,char ** argv)97 int BusyThreadsMain(int argc, char** argv) {
98 bool background = false;
99 int64_t num_threads = -1;
100 int64_t period_us = -1;
101 int64_t duty_cycle = -1;
102 uint32_t thread_name_count = 0;
103
104 static option long_options[] = {
105 {"background", no_argument, nullptr, 'd'},
106 {"threads", required_argument, nullptr, 't'},
107 {"period_us", required_argument, nullptr, 'p'},
108 {"duty_cycle", required_argument, nullptr, 'c'},
109 #if PERFETTO_HAVE_PTHREADS
110 {"thread_names", required_argument, nullptr, 'r'},
111 #endif
112 {nullptr, 0, nullptr, 0}
113 };
114 int c;
115 while ((c = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
116 switch (c) {
117 case 'd':
118 background = true;
119 break;
120 case 't':
121 num_threads = atol(optarg);
122 break;
123 case 'p':
124 period_us = atol(optarg);
125 break;
126 case 'c':
127 duty_cycle = atol(optarg);
128 break;
129 #if PERFETTO_HAVE_PTHREADS
130 case 'r':
131 thread_name_count = static_cast<uint32_t>(atoi(optarg));
132 break;
133 #endif
134 default:
135 break;
136 }
137 }
138 if (num_threads < 1 || period_us < 0 || duty_cycle < 1 || duty_cycle > 100 ||
139 thread_name_count > (1 << 20)) {
140 PrintUsage(argv[0]);
141 return 1;
142 }
143
144 if (background) {
145 pid_t pid;
146 switch (pid = fork()) {
147 case -1:
148 PERFETTO_FATAL("fork");
149 case 0: {
150 PERFETTO_CHECK(setsid() != -1);
151 base::ignore_result(chdir("/"));
152 base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY);
153 PERFETTO_CHECK(null);
154 PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1);
155 PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1);
156 PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1);
157 // Do not accidentally close stdin/stdout/stderr.
158 if (*null <= 2)
159 null.release();
160 break;
161 }
162 default:
163 printf("%d\n", pid);
164 exit(0);
165 }
166 }
167
168 int64_t busy_us =
169 static_cast<int64_t>(static_cast<double>(period_us) *
170 (static_cast<double>(duty_cycle) / 100.0));
171
172 PERFETTO_LOG("Spawning %" PRId64 " threads; period duration: %" PRId64
173 "us; busy duration: %" PRId64 "us.",
174 num_threads, period_us, busy_us);
175
176 int64_t tstart = base::GetWallTimeNs().count();
177 for (int i = 0; i < num_threads; i++) {
178 std::thread th(BusyWait, tstart, period_us, busy_us, thread_name_count);
179 th.detach();
180 }
181 PERFETTO_LOG("Threads spawned, Ctrl-C to stop.");
182 while (sleep(600))
183 ;
184
185 return 0;
186 }
187
188 } // namespace
189 } // namespace perfetto
190
main(int argc,char ** argv)191 int main(int argc, char** argv) {
192 return perfetto::BusyThreadsMain(argc, argv);
193 }
194