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