diff options
Diffstat (limited to 'latencytop')
-rw-r--r-- | latencytop/Android.mk | 26 | ||||
-rw-r--r-- | latencytop/MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | latencytop/NOTICE | 190 | ||||
-rw-r--r-- | latencytop/latencytop.c | 442 |
4 files changed, 658 insertions, 0 deletions
diff --git a/latencytop/Android.mk b/latencytop/Android.mk new file mode 100644 index 00000000..84bef410 --- /dev/null +++ b/latencytop/Android.mk @@ -0,0 +1,26 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := latencytop.c + +LOCAL_MODULE := latencytop + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) + +LOCAL_MODULE_TAGS := debug tests + +include $(BUILD_EXECUTABLE) diff --git a/latencytop/MODULE_LICENSE_APACHE2 b/latencytop/MODULE_LICENSE_APACHE2 new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/latencytop/MODULE_LICENSE_APACHE2 diff --git a/latencytop/NOTICE b/latencytop/NOTICE new file mode 100644 index 00000000..c5b1efa7 --- /dev/null +++ b/latencytop/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/latencytop/latencytop.c b/latencytop/latencytop.c new file mode 100644 index 00000000..78d7c71d --- /dev/null +++ b/latencytop/latencytop.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define MAX_LINE 512 +#define MAX_FILENAME 64 + +const char *EXPECTED_VERSION = "Latency Top version : v0.1\n"; +const char *SYSCTL_FILE = "/proc/sys/kernel/latencytop"; +const char *GLOBAL_STATS_FILE = "/proc/latency_stats"; +const char *THREAD_STATS_FILE_FORMAT = "/proc/%d/task/%d/latency"; + +struct latency_entry { + struct latency_entry *next; + unsigned long count; + unsigned long max; + unsigned long total; + char reason[MAX_LINE]; +}; + +static inline void check_latencytop() { } + +static struct latency_entry *read_global_stats(struct latency_entry *list, int erase); +static struct latency_entry *read_process_stats(struct latency_entry *list, int erase, int pid); +static struct latency_entry *read_thread_stats(struct latency_entry *list, int erase, int pid, int tid, int fatal); + +static struct latency_entry *alloc_latency_entry(void); +static void free_latency_entry(struct latency_entry *e); + +static void set_latencytop(int on); +static struct latency_entry *read_latency_file(FILE *f, struct latency_entry *list); +static void erase_latency_file(FILE *f); + +static struct latency_entry *find_latency_entry(struct latency_entry *e, char *reason); +static void print_latency_entries(struct latency_entry *head); + +static void signal_handler(int sig); +static void disable_latencytop(void); + +static int numcmp(const long long a, const long long b); +static int lat_cmp(const void *a, const void *b); + +static void clear_screen(void); +static void usage(const char *cmd); + +struct latency_entry *free_entries; + +int main(int argc, char *argv[]) { + struct latency_entry *e; + int delay, iterations; + int pid, tid; + int count, erase; + int i; + + delay = 1; + iterations = 0; + pid = tid = 0; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-d")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -d expects an argument.\n"); + exit(EXIT_FAILURE); + } + delay = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-n")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -n expects an argument.\n"); + exit(EXIT_FAILURE); + } + iterations = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-h")) { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + if (!strcmp(argv[i], "-p")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -p expects an argument.\n"); + exit(EXIT_FAILURE); + } + pid = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-t")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -t expects an argument.\n"); + exit(EXIT_FAILURE); + } + tid = atoi(argv[++i]); + continue; + } + fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (tid && !pid) { + fprintf(stderr, "If you provide a thread ID with -t, you must provide a process ID with -p.\n"); + exit(EXIT_FAILURE); + } + + check_latencytop(); + + free_entries = NULL; + + signal(SIGINT, &signal_handler); + signal(SIGTERM, &signal_handler); + + atexit(&disable_latencytop); + + set_latencytop(1); + + count = 0; + erase = 1; + + while ((iterations == 0) || (count++ < iterations)) { + + sleep(delay); + + e = NULL; + if (pid) { + if (tid) { + e = read_thread_stats(e, erase, pid, tid, 1); + } else { + e = read_process_stats(e, erase, pid); + } + } else { + e = read_global_stats(e, erase); + } + erase = 0; + + clear_screen(); + if (pid) { + if (tid) { + printf("Latencies for thread %d in process %d:\n", tid, pid); + } else { + printf("Latencies for process %d:\n", pid); + } + } else { + printf("Latencies across all processes:\n"); + } + print_latency_entries(e); + } + + set_latencytop(0); + + return 0; +} + +static struct latency_entry *read_global_stats(struct latency_entry *list, int erase) { + FILE *f; + struct latency_entry *e; + + if (erase) { + f = fopen(GLOBAL_STATS_FILE, "w"); + if (!f) { + fprintf(stderr, "Could not open global latency stats file: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fprintf(f, "erase\n"); + fclose(f); + } + + f = fopen(GLOBAL_STATS_FILE, "r"); + if (!f) { + fprintf(stderr, "Could not open global latency stats file: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + e = read_latency_file(f, list); + + fclose(f); + + return e; +} + +static struct latency_entry *read_process_stats(struct latency_entry *list, int erase, int pid) { + char dirname[MAX_FILENAME]; + DIR *dir; + struct dirent *ent; + struct latency_entry *e; + int tid; + + sprintf(dirname, "/proc/%d/task", pid); + dir = opendir(dirname); + if (!dir) { + fprintf(stderr, "Could not open task dir for process %d.\n", pid); + fprintf(stderr, "Perhaps the process has terminated?\n"); + exit(EXIT_FAILURE); + } + + e = list; + while ((ent = readdir(dir))) { + if (!isdigit(ent->d_name[0])) + continue; + + tid = atoi(ent->d_name); + + e = read_thread_stats(e, erase, pid, tid, 0); + } + + closedir(dir); + + return e; +} + +static struct latency_entry *read_thread_stats(struct latency_entry *list, int erase, int pid, int tid, int fatal) { + char filename[MAX_FILENAME]; + FILE *f; + struct latency_entry *e; + + sprintf(filename, THREAD_STATS_FILE_FORMAT, pid, tid); + + if (erase) { + f = fopen(filename, "w"); + if (!f) { + if (fatal) { + fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno)); + fprintf(stderr, "Perhaps the process or thread has terminated?\n"); + exit(EXIT_FAILURE); + } else { + return list; + } + } + fprintf(f, "erase\n"); + fclose(f); + } + + f = fopen(GLOBAL_STATS_FILE, "r"); + if (!f) { + if (fatal) { + fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno)); + fprintf(stderr, "Perhaps the process or thread has terminated?\n"); + exit(EXIT_FAILURE); + } else { + return list; + } + } + + e = read_latency_file(f, list); + + fclose(f); + + return e; +} + +static struct latency_entry *alloc_latency_entry(void) { + struct latency_entry *e; + + if (free_entries) { + e = free_entries; + free_entries = free_entries->next; + } else { + e = calloc(1, sizeof(struct latency_entry)); + if (!e) { + fprintf(stderr, "Could not allocate latency entry: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + } + + return e; +} + +static void free_latency_entry(struct latency_entry *e) { + e->next = free_entries; + free_entries = e; +} + +static struct latency_entry *find_latency_entry(struct latency_entry *head, char *reason) { + struct latency_entry *e; + + e = head; + + while (e) { + if (!strcmp(e->reason, reason)) + return e; + e = e->next; + } + + return NULL; +} + +static void set_latencytop(int on) { + FILE *f; + + f = fopen(SYSCTL_FILE, "w"); + if (!f) { + fprintf(stderr, "Could not open %s: %s\n", SYSCTL_FILE, strerror(errno)); + exit(EXIT_FAILURE); + } + + fprintf(f, "%d\n", on); + + fclose(f); +} + +static void erase_latency_file(FILE *f) { + fprintf(f, "erase\n"); +} + +static struct latency_entry *read_latency_file(FILE *f, struct latency_entry *list) { + struct latency_entry *e, *head; + char line[MAX_LINE]; + unsigned long count, max, total; + char reason[MAX_LINE]; + + head = list; + + if (!fgets(line, MAX_LINE, f)) { + fprintf(stderr, "Could not read latency file version: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + if (strcmp(line, EXPECTED_VERSION) != 0) { + fprintf(stderr, "Expected version: %s\n", EXPECTED_VERSION); + fprintf(stderr, "But got version: %s", line); + exit(EXIT_FAILURE); + } + + while (fgets(line, MAX_LINE, f)) { + sscanf(line, "%ld %ld %ld %s", &count, &total, &max, reason); + if (max > 0 || total > 0) { + e = find_latency_entry(head, reason); + if (e) { + e->count += count; + if (max > e->max) + e->max = max; + e->total += total; + } else { + e = alloc_latency_entry(); + e->count = count; + e->max = max; + e->total = total; + strcpy(e->reason, reason); + e->next = head; + head = e; + } + } + } + + return head; +} + +static void print_latency_entries(struct latency_entry *head) { + struct latency_entry *e, **array; + unsigned long average; + int i, count; + + e = head; + count = 0; + while (e) { + count++; + e = e->next; + } + + e = head; + array = calloc(count, sizeof(struct latency_entry *)); + if (!array) { + fprintf(stderr, "Error allocating array: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + for (i = 0; i < count; i++) { + array[i] = e; + e = e->next; + } + + qsort(array, count, sizeof(struct latency_entry *), &lat_cmp); + + printf("%10s %10s %7s %s\n", "Maximum", "Average", "Count", "Reason"); + for (i = 0; i < count; i++) { + e = array[i]; + average = e->total / e->count; + printf("%4lu.%02lu ms %4lu.%02lu ms %7ld %s\n", + e->max / 1000, (e->max % 1000) / 10, + average / 1000, (average % 1000) / 10, + e->count, + e->reason); + } + + free(array); +} + +static void signal_handler(int sig) { + exit(EXIT_SUCCESS); +} + +static void disable_latencytop(void) { + set_latencytop(0); +} + +static void clear_screen(void) { + printf("\n\n"); +} + +static void usage(const char *cmd) { + fprintf(stderr, "Usage: %s [ -d delay ] [ -n iterations ] [ -p pid [ -t tid ] ] [ -h ]\n" + " -d delay Time to sleep between updates.\n" + " -n iterations Number of updates to show (0 = infinite).\n" + " -p pid Process to monitor (default is all).\n" + " -t tid Thread (within specified process) to monitor (default is all).\n" + " -h Display this help screen.\n", + cmd); +} + +static int numcmp(const long long a, const long long b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; +} + +static int lat_cmp(const void *a, const void *b) { + const struct latency_entry *pa, *pb; + + pa = (*((struct latency_entry **)a)); + pb = (*((struct latency_entry **)b)); + + return numcmp(pb->max, pa->max); +} |