Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015, The Android Open Source Project |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * * Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * * Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer |
| 12 | * in the documentation and/or other materials provided with the |
| 13 | * distribution. |
| 14 | * * Neither the name of Google, Inc. nor the names of its contributors |
| 15 | * may be used to endorse or promote products derived from this |
| 16 | * software without specific prior written permission. |
| 17 | * |
| 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| 21 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| 22 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| 24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| 25 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| 26 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| 28 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 29 | * SUCH DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | #include <binder/IBinder.h> |
| 33 | #include <binder/IServiceManager.h> |
| 34 | #include <binder/Parcel.h> |
| 35 | |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 36 | #include <cutils/properties.h> |
| 37 | #include <signal.h> |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 38 | #include <stdio.h> |
| 39 | #include <stdlib.h> |
| 40 | #include <string.h> |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 41 | #include <ctime> |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 42 | |
| 43 | #include <sys/resource.h> |
| 44 | #include <sys/stat.h> |
| 45 | #include <sys/time.h> |
| 46 | #include <sys/types.h> |
| 47 | #include <unistd.h> |
| 48 | |
| 49 | #include <utils/Log.h> |
| 50 | #include <utils/String8.h> |
| 51 | #include <utils/Trace.h> |
| 52 | #include <zlib.h> |
| 53 | |
| 54 | using namespace android; |
| 55 | |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 56 | #ifdef LOG_TAG |
| 57 | #undef LOG_TAG |
| 58 | #endif |
| 59 | |
| 60 | #define LOG_TAG "anrdaemon" |
| 61 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 62 | static const int check_period = 1; // in sec |
| 63 | static const int tracing_check_period = 500000; // in micro sec |
| 64 | static const int cpu_stat_entries = 7; // number of cpu stat entries |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 65 | static const int min_buffer_size = 16; |
| 66 | static const int max_buffer_size = 2048; |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 67 | static const char* min_buffer_size_str = "16"; |
| 68 | static const char* max_buffer_size_str = "2048"; |
Zhengyin Qian | d93aa41 | 2016-06-21 14:05:44 -0700 | [diff] [blame] | 69 | static const int time_buf_size = 20; |
| 70 | static const int path_buf_size = 60; |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 71 | |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 72 | typedef struct cpu_stat { |
| 73 | unsigned long utime, ntime, stime, itime; |
| 74 | unsigned long iowtime, irqtime, sirqtime, steal; |
| 75 | unsigned long total; |
| 76 | } cpu_stat_t; |
| 77 | |
Zhengyin Qian | faf487d | 2016-04-26 11:57:16 -0700 | [diff] [blame] | 78 | /* |
| 79 | * Logging on/off threshold. |
| 80 | * Uint: 0.01%; default to 99.90% cpu. |
| 81 | */ |
| 82 | static int idle_threshold = 10; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 83 | |
| 84 | static bool quit = false; |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 85 | static bool suspend = false; |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 86 | static bool dump_requested = false; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 87 | static bool err = false; |
| 88 | static char err_msg[100]; |
| 89 | static bool tracing = false; |
| 90 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 91 | static const char* buf_size_kb = "2048"; |
| 92 | static const char* apps = ""; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 93 | static uint64_t tag = 0; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 94 | |
| 95 | static cpu_stat_t new_cpu; |
| 96 | static cpu_stat_t old_cpu; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 97 | |
| 98 | /* Log certain kernel activity when enabled */ |
| 99 | static bool log_sched = false; |
| 100 | static bool log_stack = false; |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 101 | static bool log_irq = false; |
| 102 | static bool log_sync = false; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 103 | static bool log_workq = false; |
| 104 | |
| 105 | /* Paths for debugfs controls*/ |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 106 | static const char* dfs_trace_output_path = "/d/tracing/trace"; |
| 107 | static const char* dfs_irq_path = "/d/tracing/events/irq/enable"; |
| 108 | static const char* dfs_sync_path = "/d/tracing/events/sync/enable"; |
| 109 | static const char* dfs_workq_path = "/d/tracing/events/workqueue/enable"; |
| 110 | static const char* dfs_stack_path = "/d/tracing/options/stacktrace"; |
| 111 | static const char* dfs_sched_switch_path = "/d/tracing/events/sched/sched_switch/enable"; |
| 112 | static const char* dfs_sched_wakeup_path = "/d/tracing/events/sched/sched_wakeup/enable"; |
| 113 | static const char* dfs_control_path = "/d/tracing/tracing_on"; |
| 114 | static const char* dfs_buffer_size_path = "/d/tracing/buffer_size_kb"; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 115 | static const char* dfs_tags_property = "debug.atrace.tags.enableflags"; |
| 116 | static const char* dfs_apps_property = "debug.atrace.app_cmdlines"; |
| 117 | |
| 118 | /* |
| 119 | * Read accumulated cpu data from /proc/stat |
| 120 | */ |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 121 | static void get_cpu_stat(cpu_stat_t* cpu) { |
| 122 | FILE* fp = NULL; |
| 123 | const char* params = "cpu %lu %lu %lu %lu %lu %lu %lu %*d %*d %*d\n"; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 124 | |
| 125 | if ((fp = fopen("/proc/stat", "r")) == NULL) { |
| 126 | err = true; |
Wei Wang | fc665a1 | 2016-10-12 14:35:51 -0700 | [diff] [blame] | 127 | snprintf(err_msg, sizeof(err_msg), "can't read from /proc/stat with errno %d", errno); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 128 | } else { |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 129 | if (fscanf(fp, params, &cpu->utime, &cpu->ntime, &cpu->stime, &cpu->itime, &cpu->iowtime, |
| 130 | &cpu->irqtime, &cpu->sirqtime) != cpu_stat_entries) { |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 131 | /* |
| 132 | * If failed in getting status, new_cpu won't be updated and |
| 133 | * is_heavy_loaded() will return false. |
| 134 | */ |
| 135 | ALOGE("Error in getting cpu status. Skipping this check."); |
Greg Kaiser | 2489b68 | 2016-08-11 11:40:44 -0700 | [diff] [blame] | 136 | fclose(fp); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 137 | return; |
| 138 | } |
| 139 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 140 | cpu->total = cpu->utime + cpu->ntime + cpu->stime + cpu->itime + cpu->iowtime + |
| 141 | cpu->irqtime + cpu->sirqtime; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 142 | |
| 143 | fclose(fp); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | /* |
| 148 | * Calculate cpu usage in the past interval. |
Zhengyin Qian | faf487d | 2016-04-26 11:57:16 -0700 | [diff] [blame] | 149 | * If tracing is on, increase the idle threshold by 1.00% so that we do not |
Connor Watts | 6e42381 | 2016-06-03 10:57:39 -0700 | [diff] [blame] | 150 | * turn on and off tracing frequently when the cpu load is right close to |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 151 | * threshold. |
| 152 | */ |
| 153 | static bool is_heavy_load(void) { |
| 154 | unsigned long diff_idle, diff_total; |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 155 | int threshold = idle_threshold + (tracing ? 100 : 0); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 156 | get_cpu_stat(&new_cpu); |
| 157 | diff_idle = new_cpu.itime - old_cpu.itime; |
| 158 | diff_total = new_cpu.total - old_cpu.total; |
| 159 | old_cpu = new_cpu; |
Zhengyin Qian | faf487d | 2016-04-26 11:57:16 -0700 | [diff] [blame] | 160 | return (diff_idle * 10000 < diff_total * threshold); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | /* |
| 164 | * Force the userland processes to refresh their property for logging. |
| 165 | */ |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 166 | static void dfs_poke_binder(void) { |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 167 | sp<IServiceManager> sm = defaultServiceManager(); |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 168 | Vector<String16> services = sm->listServices(); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 169 | for (size_t i = 0; i < services.size(); i++) { |
| 170 | sp<IBinder> obj = sm->checkService(services[i]); |
| 171 | if (obj != NULL) { |
| 172 | Parcel data; |
| 173 | obj->transact(IBinder::SYSPROPS_TRANSACTION, data, NULL, 0); |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | /* |
| 179 | * Enable/disable a debugfs property by writing 0/1 to its path. |
| 180 | */ |
| 181 | static int dfs_enable(bool enable, const char* path) { |
| 182 | int fd = open(path, O_WRONLY); |
| 183 | if (fd == -1) { |
| 184 | err = true; |
Wei Wang | fc665a1 | 2016-10-12 14:35:51 -0700 | [diff] [blame] | 185 | snprintf(err_msg, sizeof(err_msg), "Can't open %s. Error: %d", path, errno); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 186 | return -1; |
| 187 | } |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 188 | const char* control = (enable ? "1" : "0"); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 189 | ssize_t len = strlen(control); |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 190 | int max_try = 10; // Fail if write was interrupted for 10 times |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 191 | while (write(fd, control, len) != len) { |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 192 | if (errno == EINTR && max_try-- > 0) { |
| 193 | usleep(100); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 194 | continue; |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 195 | } |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 196 | |
| 197 | err = true; |
Wei Wang | fc665a1 | 2016-10-12 14:35:51 -0700 | [diff] [blame] | 198 | snprintf(err_msg, sizeof(err_msg), "Error %d in writing to %s.", errno, path); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 199 | } |
| 200 | close(fd); |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 201 | return (err ? -1 : 0); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | /* |
| 205 | * Set the userland tracing properties. |
| 206 | */ |
| 207 | static void dfs_set_property(uint64_t mtag, const char* mapp, bool enable) { |
| 208 | char buf[64]; |
Wei Wang | fc665a1 | 2016-10-12 14:35:51 -0700 | [diff] [blame] | 209 | snprintf(buf, sizeof(buf), "%#" PRIx64, mtag); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 210 | if (property_set(dfs_tags_property, buf) < 0) { |
| 211 | err = true; |
Wei Wang | fc665a1 | 2016-10-12 14:35:51 -0700 | [diff] [blame] | 212 | snprintf(err_msg, sizeof(err_msg), "Failed to set debug tags system properties."); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 213 | } |
| 214 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 215 | if (strlen(mapp) > 0 && property_set(dfs_apps_property, mapp) < 0) { |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 216 | err = true; |
Wei Wang | fc665a1 | 2016-10-12 14:35:51 -0700 | [diff] [blame] | 217 | snprintf(err_msg, sizeof(err_msg), "Failed to set debug applications."); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 218 | } |
| 219 | |
| 220 | if (log_sched) { |
| 221 | dfs_enable(enable, dfs_sched_switch_path); |
| 222 | dfs_enable(enable, dfs_sched_wakeup_path); |
| 223 | } |
| 224 | if (log_stack) { |
| 225 | dfs_enable(enable, dfs_stack_path); |
| 226 | } |
| 227 | if (log_irq) { |
| 228 | dfs_enable(enable, dfs_irq_path); |
| 229 | } |
| 230 | if (log_sync) { |
| 231 | dfs_enable(enable, dfs_sync_path); |
| 232 | } |
| 233 | if (log_workq) { |
| 234 | dfs_enable(enable, dfs_workq_path); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | /* |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 239 | * Dump the log in a compressed format for systrace to visualize. |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 240 | * Create a dump file "dump_of_anrdaemon.<current_time>" under /data/misc/anrd |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 241 | */ |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 242 | static void dump_trace() { |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 243 | time_t now = time(0); |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 244 | struct tm tstruct; |
Zhengyin Qian | d93aa41 | 2016-06-21 14:05:44 -0700 | [diff] [blame] | 245 | char time_buf[time_buf_size]; |
| 246 | char path_buf[path_buf_size]; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 247 | const char* header = " done\nTRACE:\n"; |
| 248 | ssize_t header_len = strlen(header); |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 249 | |
| 250 | ALOGI("Started to dump ANRdaemon trace."); |
| 251 | |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 252 | tstruct = *localtime(&now); |
Zhengyin Qian | d93aa41 | 2016-06-21 14:05:44 -0700 | [diff] [blame] | 253 | strftime(time_buf, time_buf_size, "%Y-%m-%d.%X", &tstruct); |
| 254 | snprintf(path_buf, path_buf_size, "/data/misc/anrd/dump_of_anrdaemon.%s", time_buf); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 255 | int output_fd = creat(path_buf, S_IRWXU); |
| 256 | if (output_fd == -1) { |
| 257 | ALOGE("Failed to create %s. Dump aborted.", path_buf); |
| 258 | return; |
| 259 | } |
| 260 | |
| 261 | if (write(output_fd, header, strlen(header)) != header_len) { |
| 262 | ALOGE("Failed to write the header."); |
| 263 | close(output_fd); |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | int trace_fd = open(dfs_trace_output_path, O_RDWR); |
| 268 | if (trace_fd == -1) { |
| 269 | ALOGE("Failed to open %s. Dump aborted.", dfs_trace_output_path); |
| 270 | close(output_fd); |
| 271 | return; |
| 272 | } |
| 273 | |
| 274 | z_stream zs; |
| 275 | uint8_t *in, *out; |
| 276 | int result, flush; |
| 277 | |
| 278 | memset(&zs, 0, sizeof(zs)); |
| 279 | result = deflateInit(&zs, Z_DEFAULT_COMPRESSION); |
| 280 | if (result != Z_OK) { |
| 281 | ALOGE("error initializing zlib: %d\n", result); |
| 282 | close(trace_fd); |
| 283 | close(output_fd); |
| 284 | return; |
| 285 | } |
| 286 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 287 | const size_t bufSize = 64 * 1024; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 288 | in = (uint8_t*)malloc(bufSize); |
| 289 | out = (uint8_t*)malloc(bufSize); |
| 290 | flush = Z_NO_FLUSH; |
| 291 | |
| 292 | zs.next_out = out; |
| 293 | zs.avail_out = bufSize; |
| 294 | |
| 295 | do { |
| 296 | if (zs.avail_in == 0) { |
| 297 | result = read(trace_fd, in, bufSize); |
| 298 | if (result < 0) { |
| 299 | ALOGE("error reading trace: %s", strerror(errno)); |
| 300 | result = Z_STREAM_END; |
| 301 | break; |
| 302 | } else if (result == 0) { |
| 303 | flush = Z_FINISH; |
| 304 | } else { |
| 305 | zs.next_in = in; |
| 306 | zs.avail_in = result; |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | if (zs.avail_out == 0) { |
| 311 | result = write(output_fd, out, bufSize); |
| 312 | if ((size_t)result < bufSize) { |
| 313 | ALOGE("error writing deflated trace: %s", strerror(errno)); |
| 314 | result = Z_STREAM_END; |
| 315 | zs.avail_out = bufSize; |
| 316 | break; |
| 317 | } |
| 318 | zs.next_out = out; |
| 319 | zs.avail_out = bufSize; |
| 320 | } |
| 321 | |
| 322 | } while ((result = deflate(&zs, flush)) == Z_OK); |
| 323 | |
| 324 | if (result != Z_STREAM_END) { |
| 325 | ALOGE("error deflating trace: %s\n", zs.msg); |
| 326 | } |
| 327 | |
| 328 | if (zs.avail_out < bufSize) { |
| 329 | size_t bytes = bufSize - zs.avail_out; |
| 330 | result = write(output_fd, out, bytes); |
| 331 | if ((size_t)result < bytes) { |
| 332 | ALOGE("error writing deflated trace: %s", strerror(errno)); |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | result = deflateEnd(&zs); |
| 337 | if (result != Z_OK) { |
| 338 | ALOGE("error cleaning up zlib: %d\n", result); |
| 339 | } |
| 340 | |
| 341 | free(in); |
| 342 | free(out); |
| 343 | |
| 344 | close(trace_fd); |
| 345 | close(output_fd); |
| 346 | |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 347 | ALOGI("Finished dump. Output file stored at: %s", path_buf); |
| 348 | } |
| 349 | |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 350 | /* |
| 351 | * Start logging when cpu usage is high. Meanwhile, moniter the cpu usage and |
| 352 | * stop logging when it drops down. |
| 353 | */ |
| 354 | static void start_tracing(void) { |
| 355 | ALOGD("High cpu usage, start logging."); |
| 356 | |
| 357 | if (dfs_enable(true, dfs_control_path) != 0) { |
| 358 | ALOGE("Failed to start tracing."); |
| 359 | return; |
| 360 | } |
| 361 | tracing = true; |
| 362 | |
| 363 | /* Stop logging when cpu usage drops or the daemon is suspended.*/ |
| 364 | do { |
| 365 | usleep(tracing_check_period); |
| 366 | } while (!suspend && !dump_requested && is_heavy_load()); |
| 367 | |
| 368 | if (dfs_enable(false, dfs_control_path) != 0) { |
| 369 | ALOGE("Failed to stop tracing."); |
| 370 | err = true; |
| 371 | return; |
| 372 | } |
| 373 | tracing = false; |
| 374 | |
| 375 | if (suspend) { |
| 376 | ALOGI("trace stopped due to suspend. Send SIGCONT to resume."); |
| 377 | } else if (dump_requested) { |
| 378 | ALOGI("trace stopped due to dump request."); |
| 379 | dump_trace(); |
| 380 | dump_requested = false; |
| 381 | } else { |
| 382 | ALOGD("Usage back to low, stop logging."); |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | /* |
| 387 | * Set the tracing log buffer size. |
| 388 | * Note the actual buffer size will be buf_size_kb * number of cores. |
| 389 | */ |
| 390 | static int set_tracing_buffer_size(void) { |
| 391 | int fd = open(dfs_buffer_size_path, O_WRONLY); |
| 392 | if (fd == -1) { |
| 393 | err = true; |
Wei Wang | fc665a1 | 2016-10-12 14:35:51 -0700 | [diff] [blame] | 394 | snprintf(err_msg, sizeof(err_msg), "Can't open atrace buffer size file under /d/tracing."); |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 395 | return -1; |
| 396 | } |
| 397 | ssize_t len = strlen(buf_size_kb); |
| 398 | if (write(fd, buf_size_kb, len) != len) { |
| 399 | err = true; |
Wei Wang | fc665a1 | 2016-10-12 14:35:51 -0700 | [diff] [blame] | 400 | snprintf(err_msg, sizeof(err_msg), "Error in writing to atrace buffer size file."); |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 401 | } |
| 402 | close(fd); |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 403 | return (err ? -1 : 0); |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 404 | } |
| 405 | |
| 406 | /* |
| 407 | * Main loop to moniter the cpu usage and decided whether to start logging. |
| 408 | */ |
| 409 | static void start(void) { |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 410 | if ((set_tracing_buffer_size()) != 0) return; |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 411 | |
| 412 | dfs_set_property(tag, apps, true); |
| 413 | dfs_poke_binder(); |
| 414 | |
| 415 | get_cpu_stat(&old_cpu); |
| 416 | sleep(check_period); |
| 417 | |
| 418 | while (!quit && !err) { |
| 419 | if (!suspend && is_heavy_load()) { |
| 420 | /* |
| 421 | * Increase process priority to make sure we can stop logging when |
| 422 | * necessary and do not overwrite the buffer |
| 423 | */ |
| 424 | setpriority(PRIO_PROCESS, 0, -20); |
| 425 | start_tracing(); |
| 426 | setpriority(PRIO_PROCESS, 0, 0); |
| 427 | } |
| 428 | sleep(check_period); |
| 429 | } |
| 430 | return; |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | * If trace is not running, dump trace right away. |
| 435 | * If trace is running, request to dump trace. |
| 436 | */ |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 437 | static void request_dump_trace() { |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 438 | if (!tracing) { |
| 439 | dump_trace(); |
| 440 | } else if (!dump_requested) { |
| 441 | dump_requested = true; |
| 442 | } |
| 443 | } |
| 444 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 445 | static void handle_signal(int signo) { |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 446 | switch (signo) { |
| 447 | case SIGQUIT: |
| 448 | suspend = true; |
| 449 | quit = true; |
| 450 | break; |
| 451 | case SIGSTOP: |
| 452 | suspend = true; |
| 453 | break; |
| 454 | case SIGCONT: |
| 455 | suspend = false; |
| 456 | break; |
| 457 | case SIGUSR1: |
Zhengyin Qian | b403779 | 2016-09-07 15:57:23 -0700 | [diff] [blame] | 458 | request_dump_trace(); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 459 | } |
| 460 | } |
| 461 | |
| 462 | /* |
| 463 | * Set the signal handler: |
| 464 | * SIGQUIT: Reset debugfs and tracing property and terminate the daemon. |
| 465 | * SIGSTOP: Stop logging and suspend the daemon. |
| 466 | * SIGCONT: Resume the daemon as normal. |
| 467 | * SIGUSR1: Dump the logging to a compressed format for systrace to visualize. |
| 468 | */ |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 469 | static void register_sighandler(void) { |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 470 | struct sigaction sa; |
| 471 | sigset_t block_mask; |
| 472 | |
| 473 | sigemptyset(&block_mask); |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 474 | sigaddset(&block_mask, SIGQUIT); |
| 475 | sigaddset(&block_mask, SIGSTOP); |
| 476 | sigaddset(&block_mask, SIGCONT); |
| 477 | sigaddset(&block_mask, SIGUSR1); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 478 | |
| 479 | sa.sa_flags = 0; |
| 480 | sa.sa_mask = block_mask; |
| 481 | sa.sa_handler = handle_signal; |
| 482 | sigaction(SIGQUIT, &sa, NULL); |
| 483 | sigaction(SIGSTOP, &sa, NULL); |
| 484 | sigaction(SIGCONT, &sa, NULL); |
| 485 | sigaction(SIGUSR1, &sa, NULL); |
| 486 | } |
| 487 | |
| 488 | static void show_help(void) { |
hongzhu wang | 99c043e | 2021-11-29 01:20:21 +0000 | [diff] [blame] | 489 | fprintf(stderr, "usage: anrd [options] [categoris...]\n"); |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 490 | fprintf(stdout, |
| 491 | "Options includes:\n" |
| 492 | " -a appname enable app-level tracing for a comma " |
| 493 | "separated list of cmdlines\n" |
| 494 | " -t N cpu threshold for logging to start " |
| 495 | "(uint = 0.01%%, min = 5000, max = 9999, default = 9990)\n" |
| 496 | " -s N use a trace buffer size of N KB " |
| 497 | "default to 2048KB\n" |
| 498 | " -h show helps\n"); |
| 499 | fprintf(stdout, |
| 500 | "Categoris includes:\n" |
| 501 | " am - activity manager\n" |
| 502 | " sm - sync manager\n" |
| 503 | " input - input\n" |
| 504 | " dalvik - dalvik VM\n" |
| 505 | " audio - Audio\n" |
| 506 | " gfx - Graphics\n" |
| 507 | " rs - RenderScript\n" |
| 508 | " hal - Hardware Modules\n" |
| 509 | " irq - kernel irq events\n" |
| 510 | " sched - kernel scheduler activity\n" |
| 511 | " stack - kernel stack\n" |
| 512 | " sync - kernel sync activity\n" |
| 513 | " workq - kernel work queues\n"); |
| 514 | fprintf(stdout, |
| 515 | "Control includes:\n" |
| 516 | " SIGQUIT: terminate the process\n" |
| 517 | " SIGSTOP: suspend all function of the daemon\n" |
| 518 | " SIGCONT: resume the normal function\n" |
| 519 | " SIGUSR1: dump the current logging in a compressed form\n"); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 520 | exit(0); |
| 521 | } |
| 522 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 523 | static int get_options(int argc, char* argv[]) { |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 524 | int opt = 0; |
| 525 | int threshold; |
| 526 | while ((opt = getopt(argc, argv, "a:s:t:h")) >= 0) { |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 527 | switch (opt) { |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 528 | case 'a': |
| 529 | apps = optarg; |
| 530 | break; |
| 531 | case 's': |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 532 | if (atoi(optarg) > max_buffer_size) |
| 533 | buf_size_kb = max_buffer_size_str; |
| 534 | else if (atoi(optarg) < min_buffer_size) |
| 535 | buf_size_kb = min_buffer_size_str; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 536 | else |
| 537 | buf_size_kb = optarg; |
| 538 | break; |
| 539 | case 't': |
| 540 | threshold = atoi(optarg); |
Zhengyin Qian | faf487d | 2016-04-26 11:57:16 -0700 | [diff] [blame] | 541 | if (threshold > 9999 || threshold < 5000) { |
| 542 | fprintf(stderr, "logging threshold should be 5000-9999\n"); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 543 | return 1; |
| 544 | } |
Zhengyin Qian | faf487d | 2016-04-26 11:57:16 -0700 | [diff] [blame] | 545 | idle_threshold = 10000 - threshold; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 546 | break; |
| 547 | case 'h': |
| 548 | show_help(); |
| 549 | break; |
| 550 | default: |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 551 | fprintf(stderr, |
| 552 | "Error in getting options.\n" |
| 553 | "run \"%s -h\" for usage.\n", |
| 554 | argv[0]); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 555 | return 1; |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | for (int i = optind; i < argc; i++) { |
| 560 | if (strcmp(argv[i], "am") == 0) { |
| 561 | tag |= ATRACE_TAG_ACTIVITY_MANAGER; |
| 562 | } else if (strcmp(argv[i], "input") == 0) { |
| 563 | tag |= ATRACE_TAG_INPUT; |
| 564 | } else if (strcmp(argv[i], "sm") == 0) { |
| 565 | tag |= ATRACE_TAG_SYNC_MANAGER; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 566 | } else if (strcmp(argv[i], "dalvik") == 0) { |
| 567 | tag |= ATRACE_TAG_DALVIK; |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 568 | } else if (strcmp(argv[i], "gfx") == 0) { |
| 569 | tag |= ATRACE_TAG_GRAPHICS; |
| 570 | } else if (strcmp(argv[i], "audio") == 0) { |
| 571 | tag |= ATRACE_TAG_AUDIO; |
| 572 | } else if (strcmp(argv[i], "hal") == 0) { |
| 573 | tag |= ATRACE_TAG_HAL; |
| 574 | } else if (strcmp(argv[i], "rs") == 0) { |
| 575 | tag |= ATRACE_TAG_RS; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 576 | } else if (strcmp(argv[i], "sched") == 0) { |
| 577 | log_sched = true; |
| 578 | } else if (strcmp(argv[i], "stack") == 0) { |
| 579 | log_stack = true; |
| 580 | } else if (strcmp(argv[i], "workq") == 0) { |
| 581 | log_workq = true; |
| 582 | } else if (strcmp(argv[i], "irq") == 0) { |
| 583 | log_irq = true; |
| 584 | } else if (strcmp(argv[i], "sync") == 0) { |
| 585 | log_sync = true; |
| 586 | } else { |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 587 | fprintf(stderr, |
| 588 | "invalid category: %s\n" |
| 589 | "run \"%s -h\" for usage.\n", |
| 590 | argv[i], argv[0]); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 591 | return 1; |
| 592 | } |
| 593 | } |
| 594 | |
Zhengyin Qian | 95335ba | 2016-04-26 15:14:27 -0700 | [diff] [blame] | 595 | /* If nothing is enabled, don't run */ |
| 596 | if (!tag && !log_sched && !log_stack && !log_workq && !log_irq && !log_sync) { |
| 597 | ALOGE("Specify at least one category to trace."); |
| 598 | return 1; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 599 | } |
| 600 | |
| 601 | return 0; |
| 602 | } |
| 603 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 604 | int main(int argc, char* argv[]) { |
| 605 | if (get_options(argc, argv) != 0) return 1; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 606 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 607 | if (daemon(0, 0) != 0) return 1; |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 608 | |
| 609 | register_sighandler(); |
| 610 | |
| 611 | /* Clear any the trace log file by overwrite it with a new file */ |
| 612 | int fd = creat(dfs_trace_output_path, 0); |
| 613 | if (fd == -1) { |
| 614 | ALOGE("Faield to open and cleaup previous log"); |
| 615 | return 1; |
| 616 | } |
| 617 | close(fd); |
| 618 | |
| 619 | ALOGI("ANRdaemon starting"); |
| 620 | start(); |
| 621 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 622 | if (err) ALOGE("ANRdaemon stopped due to Error: %s", err_msg); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 623 | |
| 624 | ALOGI("ANRdaemon terminated."); |
| 625 | |
Thiébaud Weksteen | 3d7bd94 | 2020-10-23 16:49:30 +0200 | [diff] [blame] | 626 | return (err ? 1 : 0); |
Zhengyin Qian | 2b28ca2 | 2015-06-18 21:37:41 -0700 | [diff] [blame] | 627 | } |