• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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