blob: d2d717accd8653019d5138fb5f2cf2c4c75554f2 [file] [log] [blame]
Colin Crossf45fa6b2012-03-26 12:38:26 -07001/*
2 * Copyright (C) 2008 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 <dirent.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <limits.h>
21#include <poll.h>
22#include <signal.h>
23#include <stdarg.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/inotify.h>
28#include <sys/stat.h>
29#include <sys/time.h>
30#include <sys/wait.h>
31#include <sys/klog.h>
32#include <time.h>
33#include <unistd.h>
John Michelaue7b6cf12013-03-07 15:35:35 -060034#include <sys/prctl.h>
Colin Crossf45fa6b2012-03-26 12:38:26 -070035
Jeff Brownbf7f4922012-06-07 16:40:01 -070036#include <cutils/debugger.h>
Colin Crossf45fa6b2012-03-26 12:38:26 -070037#include <cutils/properties.h>
38#include <cutils/sockets.h>
39#include <private/android_filesystem_config.h>
40
Robert Craig95798372013-04-04 06:33:10 -040041#include <selinux/android.h>
42
Colin Crossf45fa6b2012-03-26 12:38:26 -070043#include "dumpstate.h"
44
Jeff Brown1dc94e32014-09-11 14:15:27 -070045static const int64_t NANOS_PER_SEC = 1000000000;
46
Jeff Brownbf7f4922012-06-07 16:40:01 -070047/* list of native processes to include in the native dumps */
48static const char* native_processes_to_dump[] = {
James Dong1fc4f802012-09-10 16:08:48 -070049 "/system/bin/drmserver",
Jeff Brownbf7f4922012-06-07 16:40:01 -070050 "/system/bin/mediaserver",
51 "/system/bin/sdcard",
52 "/system/bin/surfaceflinger",
53 NULL,
54};
55
Christopher Ferris54bcc5f2015-02-10 12:15:01 -080056static uint64_t nanotime() {
57 struct timespec ts;
58 clock_gettime(CLOCK_MONOTONIC, &ts);
59 return (uint64_t)ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
60}
61
John Spurlock5ecd4be2014-01-29 14:14:40 -050062void for_each_userid(void (*func)(int), const char *header) {
Felipe Leme68116162015-11-10 20:10:25 -080063 ON_DRY_RUN_RETURN();
John Spurlock5ecd4be2014-01-29 14:14:40 -050064 DIR *d;
65 struct dirent *de;
66
67 if (header) printf("\n------ %s ------\n", header);
68 func(0);
69
70 if (!(d = opendir("/data/system/users"))) {
71 printf("Failed to open /data/system/users (%s)\n", strerror(errno));
72 return;
73 }
74
75 while ((de = readdir(d))) {
76 int userid;
77 if (de->d_type != DT_DIR || !(userid = atoi(de->d_name))) {
78 continue;
79 }
80 func(userid);
81 }
82
83 closedir(d);
84}
85
Colin Cross0c22e8b2012-11-02 15:46:56 -070086static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) {
Colin Crossf45fa6b2012-03-26 12:38:26 -070087 DIR *d;
88 struct dirent *de;
89
90 if (!(d = opendir("/proc"))) {
91 printf("Failed to open /proc (%s)\n", strerror(errno));
92 return;
93 }
94
95 printf("\n------ %s ------\n", header);
96 while ((de = readdir(d))) {
97 int pid;
98 int fd;
99 char cmdpath[255];
100 char cmdline[255];
101
102 if (!(pid = atoi(de->d_name))) {
103 continue;
104 }
105
Colin Crossf45fa6b2012-03-26 12:38:26 -0700106 memset(cmdline, 0, sizeof(cmdline));
Mark Salyzyn0751efa2016-02-05 15:33:17 -0800107
108 snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/cmdline", pid);
109 if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) {
110 TEMP_FAILURE_RETRY(read(fd, cmdline, sizeof(cmdline) - 2));
Colin Crossf45fa6b2012-03-26 12:38:26 -0700111 close(fd);
Mark Salyzyn0751efa2016-02-05 15:33:17 -0800112 if (cmdline[0]) {
113 helper(pid, cmdline, arg);
114 continue;
115 }
116 }
117
118 // if no cmdline, a kernel thread has comm
119 snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/comm", pid);
120 if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) {
121 TEMP_FAILURE_RETRY(read(fd, cmdline + 1, sizeof(cmdline) - 4));
122 close(fd);
123 if (cmdline[1]) {
124 cmdline[0] = '[';
125 size_t len = strcspn(cmdline, "\f\b\r\n");
126 cmdline[len] = ']';
127 cmdline[len+1] = '\0';
128 }
129 }
130 if (!cmdline[0]) {
131 strcpy(cmdline, "N/A");
Colin Crossf45fa6b2012-03-26 12:38:26 -0700132 }
Colin Cross0c22e8b2012-11-02 15:46:56 -0700133 helper(pid, cmdline, arg);
Colin Crossf45fa6b2012-03-26 12:38:26 -0700134 }
135
136 closedir(d);
137}
138
Colin Cross0c22e8b2012-11-02 15:46:56 -0700139static void for_each_pid_helper(int pid, const char *cmdline, void *arg) {
Felipe Leme515eb0d2015-12-14 15:09:56 -0800140 for_each_pid_func *func = (for_each_pid_func*) arg;
Colin Cross0c22e8b2012-11-02 15:46:56 -0700141 func(pid, cmdline);
142}
143
144void for_each_pid(for_each_pid_func func, const char *header) {
Felipe Leme68116162015-11-10 20:10:25 -0800145 ON_DRY_RUN_RETURN();
Felipe Leme515eb0d2015-12-14 15:09:56 -0800146 __for_each_pid(for_each_pid_helper, header, (void *) func);
Colin Cross0c22e8b2012-11-02 15:46:56 -0700147}
148
149static void for_each_tid_helper(int pid, const char *cmdline, void *arg) {
150 DIR *d;
151 struct dirent *de;
152 char taskpath[255];
Felipe Leme515eb0d2015-12-14 15:09:56 -0800153 for_each_tid_func *func = (for_each_tid_func*) arg;
Colin Cross0c22e8b2012-11-02 15:46:56 -0700154
155 sprintf(taskpath, "/proc/%d/task", pid);
156
157 if (!(d = opendir(taskpath))) {
158 printf("Failed to open %s (%s)\n", taskpath, strerror(errno));
159 return;
160 }
161
162 func(pid, pid, cmdline);
163
164 while ((de = readdir(d))) {
165 int tid;
166 int fd;
167 char commpath[255];
168 char comm[255];
169
170 if (!(tid = atoi(de->d_name))) {
171 continue;
172 }
173
174 if (tid == pid)
175 continue;
176
177 sprintf(commpath,"/proc/%d/comm", tid);
Colin Cross1493a392012-11-07 11:25:31 -0800178 memset(comm, 0, sizeof(comm));
Nick Kralevichcd67e9f2015-03-19 11:30:59 -0700179 if ((fd = TEMP_FAILURE_RETRY(open(commpath, O_RDONLY | O_CLOEXEC))) < 0) {
Colin Cross0c22e8b2012-11-02 15:46:56 -0700180 strcpy(comm, "N/A");
181 } else {
182 char *c;
Mark Salyzyn0751efa2016-02-05 15:33:17 -0800183 TEMP_FAILURE_RETRY(read(fd, comm, sizeof(comm) - 2));
Colin Cross0c22e8b2012-11-02 15:46:56 -0700184 close(fd);
185
186 c = strrchr(comm, '\n');
187 if (c) {
188 *c = '\0';
189 }
190 }
191 func(pid, tid, comm);
192 }
193
194 closedir(d);
195}
196
197void for_each_tid(for_each_tid_func func, const char *header) {
Felipe Leme68116162015-11-10 20:10:25 -0800198 ON_DRY_RUN_RETURN();
Felipe Leme515eb0d2015-12-14 15:09:56 -0800199 __for_each_pid(for_each_tid_helper, header, (void *) func);
Colin Cross0c22e8b2012-11-02 15:46:56 -0700200}
201
202void show_wchan(int pid, int tid, const char *name) {
Felipe Leme68116162015-11-10 20:10:25 -0800203 ON_DRY_RUN_RETURN();
Colin Crossf45fa6b2012-03-26 12:38:26 -0700204 char path[255];
205 char buffer[255];
Mark Salyzyn0751efa2016-02-05 15:33:17 -0800206 int fd, ret, save_errno;
Colin Cross0c22e8b2012-11-02 15:46:56 -0700207 char name_buffer[255];
Colin Crossf45fa6b2012-03-26 12:38:26 -0700208
209 memset(buffer, 0, sizeof(buffer));
210
Colin Cross0c22e8b2012-11-02 15:46:56 -0700211 sprintf(path, "/proc/%d/wchan", tid);
Nick Kralevichcd67e9f2015-03-19 11:30:59 -0700212 if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) {
Colin Crossf45fa6b2012-03-26 12:38:26 -0700213 printf("Failed to open '%s' (%s)\n", path, strerror(errno));
214 return;
215 }
216
Mark Salyzyn0751efa2016-02-05 15:33:17 -0800217 ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
218 save_errno = errno;
219 close(fd);
220
221 if (ret < 0) {
222 printf("Failed to read '%s' (%s)\n", path, strerror(save_errno));
223 return;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700224 }
225
Colin Cross0c22e8b2012-11-02 15:46:56 -0700226 snprintf(name_buffer, sizeof(name_buffer), "%*s%s",
227 pid == tid ? 0 : 3, "", name);
228
229 printf("%-7d %-32s %s\n", tid, name_buffer, buffer);
Colin Crossf45fa6b2012-03-26 12:38:26 -0700230
Mark Salyzyn0751efa2016-02-05 15:33:17 -0800231 return;
232}
233
234// print time in centiseconds
235static void snprcent(char *buffer, size_t len, size_t spc,
236 unsigned long long time) {
237 static long hz; // cache discovered hz
238
239 if (hz <= 0) {
240 hz = sysconf(_SC_CLK_TCK);
241 if (hz <= 0) {
242 hz = 1000;
243 }
244 }
245
246 // convert to centiseconds
247 time = (time * 100 + (hz / 2)) / hz;
248
249 char str[16];
250
251 snprintf(str, sizeof(str), " %llu.%02u",
252 time / 100, (unsigned)(time % 100));
253 size_t offset = strlen(buffer);
254 snprintf(buffer + offset, (len > offset) ? len - offset : 0,
255 "%*s", (spc > offset) ? (int)(spc - offset) : 0, str);
256}
257
258// print permille as a percent
259static void snprdec(char *buffer, size_t len, size_t spc, unsigned permille) {
260 char str[16];
261
262 snprintf(str, sizeof(str), " %u.%u%%", permille / 10, permille % 10);
263 size_t offset = strlen(buffer);
264 snprintf(buffer + offset, (len > offset) ? len - offset : 0,
265 "%*s", (spc > offset) ? (int)(spc - offset) : 0, str);
266}
267
268void show_showtime(int pid, const char *name) {
269 ON_DRY_RUN_RETURN();
270 char path[255];
271 char buffer[1023];
272 int fd, ret, save_errno;
273
274 memset(buffer, 0, sizeof(buffer));
275
276 sprintf(path, "/proc/%d/stat", pid);
277 if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) {
278 printf("Failed to open '%s' (%s)\n", path, strerror(errno));
279 return;
280 }
281
282 ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
283 save_errno = errno;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700284 close(fd);
Mark Salyzyn0751efa2016-02-05 15:33:17 -0800285
286 if (ret < 0) {
287 printf("Failed to read '%s' (%s)\n", path, strerror(save_errno));
288 return;
289 }
290
291 // field 14 is utime
292 // field 15 is stime
293 // field 42 is iotime
294 unsigned long long utime = 0, stime = 0, iotime = 0;
295 if (sscanf(buffer,
Mark Salyzyn791ddd32016-02-10 07:41:12 -0800296 "%*u %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d "
297 "%*d %*d %llu %llu %*d %*d %*d %*d %*d %*d "
298 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
299 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %llu ",
Mark Salyzyn0751efa2016-02-05 15:33:17 -0800300 &utime, &stime, &iotime) != 3) {
301 return;
302 }
303
304 unsigned long long total = utime + stime;
305 if (!total) {
306 return;
307 }
308
309 unsigned permille = (iotime * 1000 + (total / 2)) / total;
310 if (permille > 1000) {
311 permille = 1000;
312 }
313
314 // try to beautify and stabilize columns at <80 characters
315 snprintf(buffer, sizeof(buffer), "%-6d%s", pid, name);
316 if ((name[0] != '[') || utime) {
317 snprcent(buffer, sizeof(buffer), 57, utime);
318 }
319 snprcent(buffer, sizeof(buffer), 65, stime);
320 if ((name[0] != '[') || iotime) {
321 snprcent(buffer, sizeof(buffer), 73, iotime);
322 }
323 if (iotime) {
324 snprdec(buffer, sizeof(buffer), 79, permille);
325 }
326 puts(buffer); // adds a trailing newline
327
Colin Crossf45fa6b2012-03-26 12:38:26 -0700328 return;
329}
330
331void do_dmesg() {
332 printf("------ KERNEL LOG (dmesg) ------\n");
Felipe Leme68116162015-11-10 20:10:25 -0800333 ON_DRY_RUN_RETURN();
Elliott Hughes5f87b312012-09-17 11:43:40 -0700334 /* Get size of kernel buffer */
335 int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
Colin Crossf45fa6b2012-03-26 12:38:26 -0700336 if (size <= 0) {
337 printf("Unexpected klogctl return value: %d\n\n", size);
338 return;
339 }
340 char *buf = (char *) malloc(size + 1);
341 if (buf == NULL) {
342 printf("memory allocation failed\n\n");
343 return;
344 }
345 int retval = klogctl(KLOG_READ_ALL, buf, size);
346 if (retval < 0) {
347 printf("klogctl failure\n\n");
348 free(buf);
349 return;
350 }
351 buf[retval] = '\0';
352 printf("%s\n\n", buf);
353 free(buf);
354 return;
355}
356
357void do_showmap(int pid, const char *name) {
358 char title[255];
359 char arg[255];
360
361 sprintf(title, "SHOW MAP %d (%s)", pid, name);
362 sprintf(arg, "%d", pid);
363 run_command(title, 10, SU_PATH, "root", "showmap", arg, NULL);
364}
365
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800366static int _dump_file_from_fd(const char *title, const char *path, int fd) {
Colin Crossf45fa6b2012-03-26 12:38:26 -0700367 if (title) printf("------ %s (%s", title, path);
368
369 if (title) {
370 struct stat st;
371 if (memcmp(path, "/proc/", 6) && memcmp(path, "/sys/", 5) && !fstat(fd, &st)) {
372 char stamp[80];
373 time_t mtime = st.st_mtime;
374 strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
375 printf(": %s", stamp);
376 }
377 printf(") ------\n");
378 }
379
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800380 bool newline = false;
381 fd_set read_set;
382 struct timeval tm;
383 while (1) {
384 FD_ZERO(&read_set);
385 FD_SET(fd, &read_set);
386 /* Timeout if no data is read for 30 seconds. */
387 tm.tv_sec = 30;
388 tm.tv_usec = 0;
389 uint64_t elapsed = nanotime();
390 int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, NULL, NULL, &tm));
391 if (ret == -1) {
392 printf("*** %s: select failed: %s\n", path, strerror(errno));
393 newline = true;
394 break;
395 } else if (ret == 0) {
396 elapsed = nanotime() - elapsed;
397 printf("*** %s: Timed out after %.3fs\n", path,
398 (float) elapsed / NANOS_PER_SEC);
399 newline = true;
400 break;
401 } else {
402 char buffer[65536];
403 ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
404 if (bytes_read > 0) {
405 fwrite(buffer, bytes_read, 1, stdout);
406 newline = (buffer[bytes_read-1] == '\n');
407 } else {
408 if (bytes_read == -1) {
409 printf("*** %s: Failed to read from fd: %s", path, strerror(errno));
410 newline = true;
411 }
412 break;
413 }
Colin Crossf45fa6b2012-03-26 12:38:26 -0700414 }
Colin Crossf45fa6b2012-03-26 12:38:26 -0700415 }
Elliott Hughes997abb62015-05-15 17:05:40 -0700416 close(fd);
Christopher Ferris7dc7f322014-07-22 16:08:19 -0700417
Colin Crossf45fa6b2012-03-26 12:38:26 -0700418 if (!newline) printf("\n");
419 if (title) printf("\n");
420 return 0;
421}
422
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800423/* prints the contents of a file */
424int dump_file(const char *title, const char *path) {
Felipe Leme68116162015-11-10 20:10:25 -0800425 if (title) printf("------ %s (%s) ------\n", title, path);
426 ON_DRY_RUN_RETURN(0);
427
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800428 int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
429 if (fd < 0) {
430 int err = errno;
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800431 printf("*** %s: %s\n", path, strerror(err));
432 if (title) printf("\n");
433 return -1;
434 }
435 return _dump_file_from_fd(title, path, fd);
436}
437
Mark Salyzyn326842f2015-04-30 09:49:41 -0700438/* calls skip to gate calling dump_from_fd recursively
439 * in the specified directory. dump_from_fd defaults to
440 * dump_file_from_fd above when set to NULL. skip defaults
441 * to false when set to NULL. dump_from_fd will always be
442 * called with title NULL.
443 */
444int dump_files(const char *title, const char *dir,
445 bool (*skip)(const char *path),
446 int (*dump_from_fd)(const char *title, const char *path, int fd)) {
447 DIR *dirp;
448 struct dirent *d;
449 char *newpath = NULL;
Felipe Leme515eb0d2015-12-14 15:09:56 -0800450 const char *slash = "/";
Mark Salyzyn326842f2015-04-30 09:49:41 -0700451 int fd, retval = 0;
452
453 if (title) {
454 printf("------ %s (%s) ------\n", title, dir);
455 }
Felipe Leme68116162015-11-10 20:10:25 -0800456 ON_DRY_RUN_RETURN(0);
Mark Salyzyn326842f2015-04-30 09:49:41 -0700457
458 if (dir[strlen(dir) - 1] == '/') {
459 ++slash;
460 }
461 dirp = opendir(dir);
462 if (dirp == NULL) {
463 retval = -errno;
464 fprintf(stderr, "%s: %s\n", dir, strerror(errno));
465 return retval;
466 }
467
468 if (!dump_from_fd) {
469 dump_from_fd = dump_file_from_fd;
470 }
471 for (; ((d = readdir(dirp))); free(newpath), newpath = NULL) {
472 if ((d->d_name[0] == '.')
473 && (((d->d_name[1] == '.') && (d->d_name[2] == '\0'))
474 || (d->d_name[1] == '\0'))) {
475 continue;
476 }
477 asprintf(&newpath, "%s%s%s%s", dir, slash, d->d_name,
478 (d->d_type == DT_DIR) ? "/" : "");
479 if (!newpath) {
480 retval = -errno;
481 continue;
482 }
483 if (skip && (*skip)(newpath)) {
484 continue;
485 }
486 if (d->d_type == DT_DIR) {
487 int ret = dump_files(NULL, newpath, skip, dump_from_fd);
488 if (ret < 0) {
489 retval = ret;
490 }
491 continue;
492 }
493 fd = TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
494 if (fd < 0) {
495 retval = fd;
496 printf("*** %s: %s\n", newpath, strerror(errno));
497 continue;
498 }
499 (*dump_from_fd)(NULL, newpath, fd);
500 }
501 closedir(dirp);
502 if (title) {
503 printf("\n");
504 }
505 return retval;
506}
507
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800508/* fd must have been opened with the flag O_NONBLOCK. With this flag set,
509 * it's possible to avoid issues where opening the file itself can get
510 * stuck.
511 */
512int dump_file_from_fd(const char *title, const char *path, int fd) {
Felipe Leme68116162015-11-10 20:10:25 -0800513 ON_DRY_RUN_RETURN(0);
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800514 int flags = fcntl(fd, F_GETFL);
515 if (flags == -1) {
516 printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno));
517 return -1;
518 } else if (!(flags & O_NONBLOCK)) {
519 printf("*** %s: fd must have O_NONBLOCK set.\n", path);
520 return -1;
521 }
522 return _dump_file_from_fd(title, path, fd);
Jeff Brown1dc94e32014-09-11 14:15:27 -0700523}
524
Christopher Ferris1a9a3382015-01-30 11:00:52 -0800525bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
526 sigset_t child_mask, old_mask;
527 sigemptyset(&child_mask);
528 sigaddset(&child_mask, SIGCHLD);
529
530 if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
531 printf("*** sigprocmask failed: %s\n", strerror(errno));
532 return false;
533 }
534
535 struct timespec ts;
536 ts.tv_sec = timeout_seconds;
537 ts.tv_nsec = 0;
538 int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
539 int saved_errno = errno;
540 // Set the signals back the way they were.
541 if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
542 printf("*** sigprocmask failed: %s\n", strerror(errno));
543 if (ret == 0) {
544 return false;
545 }
546 }
547 if (ret == -1) {
548 errno = saved_errno;
549 if (errno == EAGAIN) {
550 errno = ETIMEDOUT;
551 } else {
552 printf("*** sigtimedwait failed: %s\n", strerror(errno));
553 }
554 return false;
555 }
556
557 pid_t child_pid = waitpid(pid, status, WNOHANG);
558 if (child_pid != pid) {
559 if (child_pid != -1) {
560 printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
561 } else {
562 printf("*** waitpid failed: %s\n", strerror(errno));
563 }
564 return false;
565 }
566 return true;
567}
568
Colin Crossf45fa6b2012-03-26 12:38:26 -0700569/* forks a command and waits for it to finish */
570int run_command(const char *title, int timeout_seconds, const char *command, ...) {
571 fflush(stdout);
Felipe Leme68116162015-11-10 20:10:25 -0800572
573 const char *args[1024] = {command};
574 size_t arg;
575 va_list ap;
576 va_start(ap, command);
577 if (title) printf("------ %s (%s", title, command);
578 for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
579 args[arg] = va_arg(ap, const char *);
580 if (args[arg] == NULL) break;
581 if (title) printf(" %s", args[arg]);
582 }
583 if (title) printf(") ------\n");
584 fflush(stdout);
585
586 ON_DRY_RUN_RETURN(0);
587
588 return run_command_always(title, timeout_seconds, args);
589}
590
591/* forks a command and waits for it to finish */
592int run_command_always(const char *title, int timeout_seconds, const char *args[]) {
593 const char *command = args[0];
594
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800595 uint64_t start = nanotime();
Colin Crossf45fa6b2012-03-26 12:38:26 -0700596 pid_t pid = fork();
597
598 /* handle error case */
599 if (pid < 0) {
600 printf("*** fork: %s\n", strerror(errno));
601 return pid;
602 }
603
604 /* handle child case */
605 if (pid == 0) {
Colin Crossf45fa6b2012-03-26 12:38:26 -0700606
John Michelaue7b6cf12013-03-07 15:35:35 -0600607 /* make sure the child dies when dumpstate dies */
608 prctl(PR_SET_PDEATHSIG, SIGKILL);
609
Andres Morales2e671bb2014-08-21 12:38:22 -0700610 /* just ignore SIGPIPE, will go down with parent's */
611 struct sigaction sigact;
612 memset(&sigact, 0, sizeof(sigact));
613 sigact.sa_handler = SIG_IGN;
614 sigaction(SIGPIPE, &sigact, NULL);
615
Colin Crossf45fa6b2012-03-26 12:38:26 -0700616 execvp(command, (char**) args);
617 printf("*** exec(%s): %s\n", command, strerror(errno));
618 fflush(stdout);
619 _exit(-1);
620 }
621
622 /* handle parent case */
Christopher Ferris1a9a3382015-01-30 11:00:52 -0800623 int status;
624 bool ret = waitpid_with_timeout(pid, timeout_seconds, &status);
625 uint64_t elapsed = nanotime() - start;
626 if (!ret) {
627 if (errno == ETIMEDOUT) {
628 printf("*** %s: Timed out after %.3fs (killing pid %d)\n", command,
629 (float) elapsed / NANOS_PER_SEC, pid);
630 } else {
631 printf("*** %s: Error after %.4fs (killing pid %d)\n", command,
632 (float) elapsed / NANOS_PER_SEC, pid);
633 }
634 kill(pid, SIGTERM);
635 if (!waitpid_with_timeout(pid, 5, NULL)) {
636 kill(pid, SIGKILL);
637 if (!waitpid_with_timeout(pid, 5, NULL)) {
638 printf("*** %s: Cannot kill %d even with SIGKILL.\n", command, pid);
Colin Crossf45fa6b2012-03-26 12:38:26 -0700639 }
Colin Crossf45fa6b2012-03-26 12:38:26 -0700640 }
Christopher Ferris1a9a3382015-01-30 11:00:52 -0800641 return -1;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700642 }
Christopher Ferris1a9a3382015-01-30 11:00:52 -0800643
644 if (WIFSIGNALED(status)) {
645 printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
646 } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
647 printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
648 }
649 if (title) printf("[%s: %.3fs elapsed]\n\n", command, (float)elapsed / NANOS_PER_SEC);
650
651 return status;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700652}
653
654size_t num_props = 0;
655static char* props[2000];
656
657static void print_prop(const char *key, const char *name, void *user) {
658 (void) user;
659 if (num_props < sizeof(props) / sizeof(props[0])) {
660 char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
661 snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
662 props[num_props++] = strdup(buf);
663 }
664}
665
666static int compare_prop(const void *a, const void *b) {
667 return strcmp(*(char * const *) a, *(char * const *) b);
668}
669
670/* prints all the system properties */
671void print_properties() {
Felipe Leme68116162015-11-10 20:10:25 -0800672 printf("------ SYSTEM PROPERTIES ------\n");
673 ON_DRY_RUN_RETURN();
Colin Crossf45fa6b2012-03-26 12:38:26 -0700674 size_t i;
675 num_props = 0;
676 property_list(print_prop, NULL);
677 qsort(&props, num_props, sizeof(props[0]), compare_prop);
678
Colin Crossf45fa6b2012-03-26 12:38:26 -0700679 for (i = 0; i < num_props; ++i) {
680 fputs(props[i], stdout);
681 free(props[i]);
682 }
683 printf("\n");
684}
685
686/* redirect output to a service control socket */
687void redirect_to_socket(FILE *redirect, const char *service) {
688 int s = android_get_control_socket(service);
689 if (s < 0) {
690 fprintf(stderr, "android_get_control_socket(%s): %s\n", service, strerror(errno));
691 exit(1);
692 }
Nick Kralevichcd67e9f2015-03-19 11:30:59 -0700693 fcntl(s, F_SETFD, FD_CLOEXEC);
Colin Crossf45fa6b2012-03-26 12:38:26 -0700694 if (listen(s, 4) < 0) {
695 fprintf(stderr, "listen(control socket): %s\n", strerror(errno));
696 exit(1);
697 }
698
699 struct sockaddr addr;
700 socklen_t alen = sizeof(addr);
701 int fd = accept(s, &addr, &alen);
702 if (fd < 0) {
703 fprintf(stderr, "accept(control socket): %s\n", strerror(errno));
704 exit(1);
705 }
706
707 fflush(redirect);
708 dup2(fd, fileno(redirect));
709 close(fd);
710}
711
Christopher Ferrisff4a4dc2015-02-09 16:24:47 -0800712/* redirect output to a file */
713void redirect_to_file(FILE *redirect, char *path) {
Colin Crossf45fa6b2012-03-26 12:38:26 -0700714 char *chp = path;
715
716 /* skip initial slash */
717 if (chp[0] == '/')
718 chp++;
719
720 /* create leading directories, if necessary */
721 while (chp && chp[0]) {
722 chp = strchr(chp, '/');
723 if (chp) {
724 *chp = 0;
Jeff Sharkey27f9e6d2013-03-13 15:45:50 -0700725 mkdir(path, 0770); /* drwxrwx--- */
Colin Crossf45fa6b2012-03-26 12:38:26 -0700726 *chp++ = '/';
727 }
728 }
729
Nick Kralevichcd67e9f2015-03-19 11:30:59 -0700730 int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
Christopher Ferrisff4a4dc2015-02-09 16:24:47 -0800731 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
Colin Crossf45fa6b2012-03-26 12:38:26 -0700732 if (fd < 0) {
733 fprintf(stderr, "%s: %s\n", path, strerror(errno));
734 exit(1);
735 }
736
Christopher Ferrisff4a4dc2015-02-09 16:24:47 -0800737 TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect)));
Colin Crossf45fa6b2012-03-26 12:38:26 -0700738 close(fd);
Colin Crossf45fa6b2012-03-26 12:38:26 -0700739}
740
Jeff Brownbf7f4922012-06-07 16:40:01 -0700741static bool should_dump_native_traces(const char* path) {
742 for (const char** p = native_processes_to_dump; *p; p++) {
743 if (!strcmp(*p, path)) {
744 return true;
745 }
746 }
747 return false;
748}
749
750/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
751const char *dump_traces() {
Felipe Leme68116162015-11-10 20:10:25 -0800752 ON_DRY_RUN_RETURN(NULL);
Jeff Brownbf7f4922012-06-07 16:40:01 -0700753 const char* result = NULL;
754
Colin Crossf45fa6b2012-03-26 12:38:26 -0700755 char traces_path[PROPERTY_VALUE_MAX] = "";
756 property_get("dalvik.vm.stack-trace-file", traces_path, "");
757 if (!traces_path[0]) return NULL;
758
759 /* move the old traces.txt (if any) out of the way temporarily */
760 char anr_traces_path[PATH_MAX];
761 strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
762 strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
763 if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
764 fprintf(stderr, "rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
765 return NULL; // Can't rename old traces.txt -- no permission? -- leave it alone instead
766 }
767
Colin Crossf45fa6b2012-03-26 12:38:26 -0700768 /* create a new, empty traces.txt file to receive stack dumps */
Nick Kralevichcd67e9f2015-03-19 11:30:59 -0700769 int fd = TEMP_FAILURE_RETRY(open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800770 0666)); /* -rw-rw-rw- */
Colin Crossf45fa6b2012-03-26 12:38:26 -0700771 if (fd < 0) {
772 fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
773 return NULL;
774 }
Nick Kralevichc7f1fe22012-04-06 09:31:28 -0700775 int chmod_ret = fchmod(fd, 0666);
776 if (chmod_ret < 0) {
777 fprintf(stderr, "fchmod on %s failed: %s\n", traces_path, strerror(errno));
778 close(fd);
779 return NULL;
780 }
Colin Crossf45fa6b2012-03-26 12:38:26 -0700781
Felipe Leme515eb0d2015-12-14 15:09:56 -0800782 /* Variables below must be initialized before 'goto' statements */
783 int dalvik_found = 0;
784 int ifd, wfd = -1;
785
Colin Crossf45fa6b2012-03-26 12:38:26 -0700786 /* walk /proc and kill -QUIT all Dalvik processes */
787 DIR *proc = opendir("/proc");
788 if (proc == NULL) {
789 fprintf(stderr, "/proc: %s\n", strerror(errno));
Jeff Brownbf7f4922012-06-07 16:40:01 -0700790 goto error_close_fd;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700791 }
792
793 /* use inotify to find when processes are done dumping */
Felipe Leme515eb0d2015-12-14 15:09:56 -0800794 ifd = inotify_init();
Colin Crossf45fa6b2012-03-26 12:38:26 -0700795 if (ifd < 0) {
796 fprintf(stderr, "inotify_init: %s\n", strerror(errno));
Jeff Brownbf7f4922012-06-07 16:40:01 -0700797 goto error_close_fd;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700798 }
799
Felipe Leme515eb0d2015-12-14 15:09:56 -0800800 wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
Colin Crossf45fa6b2012-03-26 12:38:26 -0700801 if (wfd < 0) {
802 fprintf(stderr, "inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
Jeff Brownbf7f4922012-06-07 16:40:01 -0700803 goto error_close_ifd;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700804 }
805
806 struct dirent *d;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700807 while ((d = readdir(proc))) {
808 int pid = atoi(d->d_name);
809 if (pid <= 0) continue;
810
Jeff Brownbf7f4922012-06-07 16:40:01 -0700811 char path[PATH_MAX];
812 char data[PATH_MAX];
Colin Crossf45fa6b2012-03-26 12:38:26 -0700813 snprintf(path, sizeof(path), "/proc/%d/exe", pid);
Jeff Brownbf7f4922012-06-07 16:40:01 -0700814 ssize_t len = readlink(path, data, sizeof(data) - 1);
815 if (len <= 0) {
Colin Crossf45fa6b2012-03-26 12:38:26 -0700816 continue;
817 }
Jeff Brownbf7f4922012-06-07 16:40:01 -0700818 data[len] = '\0';
Colin Crossf45fa6b2012-03-26 12:38:26 -0700819
Colin Cross0d6180f2014-07-16 19:00:46 -0700820 if (!strncmp(data, "/system/bin/app_process", strlen("/system/bin/app_process"))) {
Jeff Brownbf7f4922012-06-07 16:40:01 -0700821 /* skip zygote -- it won't dump its stack anyway */
822 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
Nick Kralevichcd67e9f2015-03-19 11:30:59 -0700823 int cfd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC));
Jeff Brown1dc94e32014-09-11 14:15:27 -0700824 len = read(cfd, data, sizeof(data) - 1);
825 close(cfd);
Jeff Brownbf7f4922012-06-07 16:40:01 -0700826 if (len <= 0) {
827 continue;
828 }
829 data[len] = '\0';
Colin Cross0d6180f2014-07-16 19:00:46 -0700830 if (!strncmp(data, "zygote", strlen("zygote"))) {
Jeff Brownbf7f4922012-06-07 16:40:01 -0700831 continue;
832 }
833
834 ++dalvik_found;
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800835 uint64_t start = nanotime();
Jeff Brownbf7f4922012-06-07 16:40:01 -0700836 if (kill(pid, SIGQUIT)) {
837 fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
838 continue;
839 }
840
841 /* wait for the writable-close notification from inotify */
842 struct pollfd pfd = { ifd, POLLIN, 0 };
Nick Vaccaro85453ec2014-04-30 11:19:23 -0700843 int ret = poll(&pfd, 1, 5000); /* 5 sec timeout */
Jeff Brownbf7f4922012-06-07 16:40:01 -0700844 if (ret < 0) {
845 fprintf(stderr, "poll: %s\n", strerror(errno));
846 } else if (ret == 0) {
847 fprintf(stderr, "warning: timed out dumping pid %d\n", pid);
848 } else {
849 struct inotify_event ie;
850 read(ifd, &ie, sizeof(ie));
851 }
Jeff Brown1dc94e32014-09-11 14:15:27 -0700852
853 if (lseek(fd, 0, SEEK_END) < 0) {
854 fprintf(stderr, "lseek: %s\n", strerror(errno));
855 } else {
Christopher Ferris31ef8552015-01-14 13:23:30 -0800856 dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n",
Jeff Brown1dc94e32014-09-11 14:15:27 -0700857 pid, (float)(nanotime() - start) / NANOS_PER_SEC);
Jeff Brown1dc94e32014-09-11 14:15:27 -0700858 }
Jeff Brownbf7f4922012-06-07 16:40:01 -0700859 } else if (should_dump_native_traces(data)) {
860 /* dump native process if appropriate */
861 if (lseek(fd, 0, SEEK_END) < 0) {
862 fprintf(stderr, "lseek: %s\n", strerror(errno));
863 } else {
Christopher Ferris31ef8552015-01-14 13:23:30 -0800864 static uint16_t timeout_failures = 0;
Christopher Ferris54bcc5f2015-02-10 12:15:01 -0800865 uint64_t start = nanotime();
Christopher Ferris31ef8552015-01-14 13:23:30 -0800866
867 /* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
868 if (timeout_failures == 3) {
869 dprintf(fd, "too many stack dump failures, skipping...\n");
870 } else if (dump_backtrace_to_file_timeout(pid, fd, 20) == -1) {
871 dprintf(fd, "dumping failed, likely due to a timeout\n");
872 timeout_failures++;
873 } else {
874 timeout_failures = 0;
875 }
876 dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n",
Jeff Brown1dc94e32014-09-11 14:15:27 -0700877 pid, (float)(nanotime() - start) / NANOS_PER_SEC);
Jeff Brownbf7f4922012-06-07 16:40:01 -0700878 }
Colin Crossf45fa6b2012-03-26 12:38:26 -0700879 }
880 }
881
Colin Crossf45fa6b2012-03-26 12:38:26 -0700882 if (dalvik_found == 0) {
883 fprintf(stderr, "Warning: no Dalvik processes found to dump stacks\n");
884 }
885
886 static char dump_traces_path[PATH_MAX];
887 strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
888 strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
889 if (rename(traces_path, dump_traces_path)) {
890 fprintf(stderr, "rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
Jeff Brownbf7f4922012-06-07 16:40:01 -0700891 goto error_close_ifd;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700892 }
Jeff Brownbf7f4922012-06-07 16:40:01 -0700893 result = dump_traces_path;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700894
895 /* replace the saved [ANR] traces.txt file */
896 rename(anr_traces_path, traces_path);
Jeff Brownbf7f4922012-06-07 16:40:01 -0700897
898error_close_ifd:
899 close(ifd);
900error_close_fd:
901 close(fd);
902 return result;
Colin Crossf45fa6b2012-03-26 12:38:26 -0700903}
904
Sreeram Ramachandran2b3bba32014-07-08 15:40:55 -0700905void dump_route_tables() {
Felipe Leme68116162015-11-10 20:10:25 -0800906 ON_DRY_RUN_RETURN();
Sreeram Ramachandran2b3bba32014-07-08 15:40:55 -0700907 const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
908 dump_file("RT_TABLES", RT_TABLES_PATH);
Nick Kralevichcd67e9f2015-03-19 11:30:59 -0700909 FILE* fp = fopen(RT_TABLES_PATH, "re");
Sreeram Ramachandran2b3bba32014-07-08 15:40:55 -0700910 if (!fp) {
911 printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno));
912 return;
913 }
914 char table[16];
915 // Each line has an integer (the table number), a space, and a string (the table name). We only
916 // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name.
917 // Add a fixed max limit so this doesn't go awry.
918 for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) {
919 run_command("ROUTE TABLE IPv4", 10, "ip", "-4", "route", "show", "table", table, NULL);
920 run_command("ROUTE TABLE IPv6", 10, "ip", "-6", "route", "show", "table", table, NULL);
921 }
922 fclose(fp);
923}
Mark Salyzyn8c8130e2015-12-09 11:21:28 -0800924
925void dump_emmc_ecsd(const char *ext_csd_path) {
926 static const size_t EXT_CSD_REV = 192;
927 static const size_t EXT_PRE_EOL_INFO = 267;
928 static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_A = 268;
929 static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_B = 269;
930 struct hex {
931 char str[2];
932 } buffer[512];
933 int fd, ext_csd_rev, ext_pre_eol_info;
934 ssize_t bytes_read;
935 static const char *ver_str[] = {
936 "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
937 };
938 static const char *eol_str[] = {
939 "Undefined",
940 "Normal",
941 "Warning (consumed 80% of reserve)",
Mark Salyzyn4b45d672015-12-11 10:41:52 -0800942 "Urgent (consumed 90% of reserve)"
Mark Salyzyn8c8130e2015-12-09 11:21:28 -0800943 };
944
945 printf("------ %s Extended CSD ------\n", ext_csd_path);
946
947 fd = TEMP_FAILURE_RETRY(open(ext_csd_path,
948 O_RDONLY | O_NONBLOCK | O_CLOEXEC));
949 if (fd < 0) {
950 printf("*** %s: %s\n\n", ext_csd_path, strerror(errno));
951 return;
952 }
953
954 bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
955 close(fd);
956 if (bytes_read < 0) {
957 printf("*** %s: %s\n\n", ext_csd_path, strerror(errno));
958 return;
959 }
Mark Salyzyn4b45d672015-12-11 10:41:52 -0800960 if (bytes_read < (ssize_t)(EXT_CSD_REV * sizeof(struct hex))) {
Mark Salyzyn8c8130e2015-12-09 11:21:28 -0800961 printf("*** %s: truncated content %zd\n\n", ext_csd_path, bytes_read);
962 return;
963 }
964
965 ext_csd_rev = 0;
966 if (sscanf(buffer[EXT_CSD_REV].str, "%02x", &ext_csd_rev) != 1) {
967 printf("*** %s: EXT_CSD_REV parse error \"%.2s\"\n\n",
968 ext_csd_path, buffer[EXT_CSD_REV].str);
969 return;
970 }
971
972 printf("rev 1.%d (MMC %s)\n",
973 ext_csd_rev,
974 (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
975 ver_str[ext_csd_rev] :
976 "Unknown");
977 if (ext_csd_rev < 7) {
978 printf("\n");
979 return;
980 }
981
Mark Salyzyn4b45d672015-12-11 10:41:52 -0800982 if (bytes_read < (ssize_t)(EXT_PRE_EOL_INFO * sizeof(struct hex))) {
Mark Salyzyn8c8130e2015-12-09 11:21:28 -0800983 printf("*** %s: truncated content %zd\n\n", ext_csd_path, bytes_read);
984 return;
985 }
986
987 ext_pre_eol_info = 0;
988 if (sscanf(buffer[EXT_PRE_EOL_INFO].str, "%02x", &ext_pre_eol_info) != 1) {
989 printf("*** %s: PRE_EOL_INFO parse error \"%.2s\"\n\n",
990 ext_csd_path, buffer[EXT_PRE_EOL_INFO].str);
991 return;
992 }
993 printf("PRE_EOL_INFO %d (MMC %s)\n",
994 ext_pre_eol_info,
995 eol_str[(ext_pre_eol_info < (int)
996 (sizeof(eol_str) / sizeof(eol_str[0]))) ?
997 ext_pre_eol_info : 0]);
998
999 for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A;
1000 lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B;
1001 ++lifetime) {
1002 int ext_device_life_time_est;
1003 static const char *est_str[] = {
1004 "Undefined",
1005 "0-10% of device lifetime used",
1006 "10-20% of device lifetime used",
1007 "20-30% of device lifetime used",
1008 "30-40% of device lifetime used",
1009 "40-50% of device lifetime used",
1010 "50-60% of device lifetime used",
1011 "60-70% of device lifetime used",
1012 "70-80% of device lifetime used",
1013 "80-90% of device lifetime used",
1014 "90-100% of device lifetime used",
1015 "Exceeded the maximum estimated device lifetime",
1016 };
1017
Mark Salyzyn4b45d672015-12-11 10:41:52 -08001018 if (bytes_read < (ssize_t)(lifetime * sizeof(struct hex))) {
Mark Salyzyn8c8130e2015-12-09 11:21:28 -08001019 printf("*** %s: truncated content %zd\n", ext_csd_path, bytes_read);
1020 break;
1021 }
1022
1023 ext_device_life_time_est = 0;
1024 if (sscanf(buffer[lifetime].str, "%02x", &ext_device_life_time_est) != 1) {
1025 printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%.2s\"\n",
1026 ext_csd_path,
1027 (unsigned)(lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) + 'A',
1028 buffer[lifetime].str);
1029 continue;
1030 }
1031 printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n",
1032 (unsigned)(lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) + 'A',
1033 ext_device_life_time_est,
1034 est_str[(ext_device_life_time_est < (int)
1035 (sizeof(est_str) / sizeof(est_str[0]))) ?
1036 ext_device_life_time_est : 0]);
1037 }
1038
1039 printf("\n");
1040}