OSDN Git Service

Merge "Setup signal handler before any exec command"
[android-x86/system-core.git] / logd / LogStatistics.cpp
1 /*
2  * Copyright (C) 2014 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 <algorithm> // std::max
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 #include <log/logger.h>
24 #include <private/android_filesystem_config.h>
25 #include <utils/String8.h>
26
27 #include "LogStatistics.h"
28
29 LogStatistics::LogStatistics()
30         : enable(false) {
31     log_id_for_each(id) {
32         mSizes[id] = 0;
33         mElements[id] = 0;
34         mSizesTotal[id] = 0;
35         mElementsTotal[id] = 0;
36     }
37 }
38
39 namespace android {
40
41 // caller must own and free character string
42 static char *pidToName(pid_t pid) {
43     char *retval = NULL;
44     if (pid == 0) { // special case from auditd for kernel
45         retval = strdup("logd.auditd");
46     } else {
47         char buffer[512];
48         snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
49         int fd = open(buffer, O_RDONLY);
50         if (fd >= 0) {
51             ssize_t ret = read(fd, buffer, sizeof(buffer));
52             if (ret > 0) {
53                 buffer[sizeof(buffer)-1] = '\0';
54                 // frameworks intermediate state
55                 if (strcmp(buffer, "<pre-initialized>")) {
56                     retval = strdup(buffer);
57                 }
58             }
59             close(fd);
60         }
61     }
62     return retval;
63 }
64
65 }
66
67 void LogStatistics::add(LogBufferElement *e) {
68     log_id_t log_id = e->getLogId();
69     unsigned short size = e->getMsgLen();
70     mSizes[log_id] += size;
71     ++mElements[log_id];
72
73     uid_t uid = e->getUid();
74     unsigned short dropped = e->getDropped();
75     android::hash_t hash = android::hash_type(uid);
76     uidTable_t &table = uidTable[log_id];
77     ssize_t index = table.find(-1, hash, uid);
78     if (index == -1) {
79         UidEntry initEntry(uid);
80         initEntry.add(size);
81         initEntry.add_dropped(dropped);
82         table.add(hash, initEntry);
83     } else {
84         UidEntry &entry = table.editEntryAt(index);
85         entry.add(size);
86         entry.add_dropped(dropped);
87     }
88
89     mSizesTotal[log_id] += size;
90     ++mElementsTotal[log_id];
91
92     if (!enable) {
93         return;
94     }
95
96     pid_t pid = e->getPid();
97     hash = android::hash_type(pid);
98     index = pidTable.find(-1, hash, pid);
99     if (index == -1) {
100         PidEntry initEntry(pid, uid, android::pidToName(pid));
101         initEntry.add(size);
102         initEntry.add_dropped(dropped);
103         pidTable.add(hash, initEntry);
104     } else {
105         PidEntry &entry = pidTable.editEntryAt(index);
106         if (entry.getUid() != uid) {
107             entry.setUid(uid);
108             entry.setName(android::pidToName(pid));
109         } else if (!entry.getName()) {
110             char *name = android::pidToName(pid);
111             if (name) {
112                 entry.setName(name);
113             }
114         }
115         entry.add(size);
116         entry.add_dropped(dropped);
117     }
118 }
119
120 void LogStatistics::subtract(LogBufferElement *e) {
121     log_id_t log_id = e->getLogId();
122     unsigned short size = e->getMsgLen();
123     mSizes[log_id] -= size;
124     --mElements[log_id];
125
126     uid_t uid = e->getUid();
127     unsigned short dropped = e->getDropped();
128     android::hash_t hash = android::hash_type(uid);
129     uidTable_t &table = uidTable[log_id];
130     ssize_t index = table.find(-1, hash, uid);
131     if (index != -1) {
132         UidEntry &entry = table.editEntryAt(index);
133         if (entry.subtract(size) || entry.subtract_dropped(dropped)) {
134             table.removeAt(index);
135         }
136     }
137
138     if (!enable) {
139         return;
140     }
141
142     pid_t pid = e->getPid();
143     hash = android::hash_type(pid);
144     index = pidTable.find(-1, hash, pid);
145     if (index != -1) {
146         PidEntry &entry = pidTable.editEntryAt(index);
147         if (entry.subtract(size) || entry.subtract_dropped(dropped)) {
148             pidTable.removeAt(index);
149         }
150     }
151 }
152
153 // Atomically set an entry to drop
154 // entry->setDropped(1) must follow this call, caller should do this explicitly.
155 void LogStatistics::drop(LogBufferElement *e) {
156     log_id_t log_id = e->getLogId();
157     unsigned short size = e->getMsgLen();
158     mSizes[log_id] -= size;
159
160     uid_t uid = e->getUid();
161     android::hash_t hash = android::hash_type(uid);
162     typeof uidTable[0] &table = uidTable[log_id];
163     ssize_t index = table.find(-1, hash, uid);
164     if (index != -1) {
165         UidEntry &entry = table.editEntryAt(index);
166         entry.subtract(size);
167         entry.add_dropped(1);
168     }
169
170     if (!enable) {
171         return;
172     }
173
174     pid_t pid = e->getPid();
175     hash = android::hash_type(pid);
176     index = pidTable.find(-1, hash, pid);
177     if (index != -1) {
178         PidEntry &entry = pidTable.editEntryAt(index);
179         entry.subtract(size);
180         entry.add_dropped(1);
181     }
182 }
183
184 // caller must own and free character string
185 char *LogStatistics::uidToName(uid_t uid) {
186     // Local hard coded favourites
187     if (uid == AID_LOGD) {
188         return strdup("auditd");
189     }
190
191     // Android hard coded
192     const struct android_id_info *info = android_ids;
193
194     for (size_t i = 0; i < android_id_count; ++i) {
195         if (info->aid == uid) {
196             return strdup(info->name);
197         }
198         ++info;
199     }
200
201     // Parse /data/system/packages.list
202     char *name = android::uidToName(uid);
203     if (name) {
204         return name;
205     }
206
207     // report uid -> pid(s) -> pidToName if unique
208     ssize_t index = -1;
209     while ((index = pidTable.next(index)) != -1) {
210         const PidEntry &entry = pidTable.entryAt(index);
211
212         if (entry.getUid() == uid) {
213             const char *n = entry.getName();
214
215             if (n) {
216                 if (!name) {
217                     name = strdup(n);
218                 } else if (strcmp(name, n)) {
219                     free(name);
220                     return NULL;
221                 }
222             }
223         }
224     }
225
226     // No one
227     return name;
228 }
229
230 static void format_line(android::String8 &output,
231         android::String8 &name, android::String8 &size, android::String8 &pruned) {
232     static const size_t pruned_len = 6;
233     static const size_t total_len = 70 + pruned_len;
234
235     ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
236     ssize_t size_len = std::max(size.length() + 1,
237                                 total_len - name.length() - drop_len - 1);
238
239     if (pruned.length()) {
240         output.appendFormat("%s%*s%*s\n", name.string(),
241                                           (int)size_len, size.string(),
242                                           (int)drop_len, pruned.string());
243     } else {
244         output.appendFormat("%s%*s\n", name.string(),
245                                        (int)size_len, size.string());
246     }
247 }
248
249 void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
250     static const unsigned short spaces_total = 19;
251
252     if (*buf) {
253         free(*buf);
254         *buf = NULL;
255     }
256
257     // Report on total logging, current and for all time
258
259     android::String8 output("size/num");
260     size_t oldLength;
261     short spaces = 1;
262
263     log_id_for_each(id) {
264         if (!(logMask & (1 << id))) {
265             continue;
266         }
267         oldLength = output.length();
268         if (spaces < 0) {
269             spaces = 0;
270         }
271         output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
272         spaces += spaces_total + oldLength - output.length();
273     }
274
275     spaces = 4;
276     output.appendFormat("\nTotal");
277
278     log_id_for_each(id) {
279         if (!(logMask & (1 << id))) {
280             continue;
281         }
282         oldLength = output.length();
283         if (spaces < 0) {
284             spaces = 0;
285         }
286         output.appendFormat("%*s%zu/%zu", spaces, "",
287                             sizesTotal(id), elementsTotal(id));
288         spaces += spaces_total + oldLength - output.length();
289     }
290
291     spaces = 6;
292     output.appendFormat("\nNow");
293
294     log_id_for_each(id) {
295         if (!(logMask & (1 << id))) {
296             continue;
297         }
298
299         size_t els = elements(id);
300         if (els) {
301             oldLength = output.length();
302             if (spaces < 0) {
303                 spaces = 0;
304             }
305             output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
306             spaces -= output.length() - oldLength;
307         }
308         spaces += spaces_total;
309     }
310
311     // Report on Chattiest
312
313     // Chattiest by application (UID)
314     static const size_t maximum_sorted_entries = 32;
315     log_id_for_each(id) {
316         if (!(logMask & (1 << id))) {
317             continue;
318         }
319
320         bool headerPrinted = false;
321         std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
322         ssize_t index = -1;
323         while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
324             const UidEntry *entry = sorted[index];
325             uid_t u = entry->getKey();
326             if ((uid != AID_ROOT) && (u != uid)) {
327                 continue;
328             }
329
330             if (!headerPrinted) {
331                 output.appendFormat("\n\n");
332                 android::String8 name("");
333                 if (uid == AID_ROOT) {
334                     name.appendFormat(
335                         "Chattiest UIDs in %s log buffer:",
336                         android_log_id_to_name(id));
337                 } else {
338                     name.appendFormat(
339                         "Logging for your UID in %s log buffer:",
340                         android_log_id_to_name(id));
341                 }
342                 android::String8 size("Size");
343                 android::String8 pruned("Pruned");
344                 if (!worstUidEnabledForLogid(id)) {
345                     pruned.setTo("");
346                 }
347                 format_line(output, name, size, pruned);
348
349                 name.setTo("UID   PACKAGE");
350                 size.setTo("BYTES");
351                 pruned.setTo("LINES");
352                 if (!worstUidEnabledForLogid(id)) {
353                     pruned.setTo("");
354                 }
355                 format_line(output, name, size, pruned);
356
357                 headerPrinted = true;
358             }
359
360             android::String8 name("");
361             name.appendFormat("%u", u);
362             char *n = uidToName(u);
363             if (n) {
364                 name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
365                 free(n);
366             }
367
368             android::String8 size("");
369             size.appendFormat("%zu", entry->getSizes());
370
371             android::String8 pruned("");
372             size_t dropped = entry->getDropped();
373             if (dropped) {
374                 pruned.appendFormat("%zu", dropped);
375             }
376
377             format_line(output, name, size, pruned);
378         }
379     }
380
381     if (enable) {
382         bool headerPrinted = false;
383         std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
384         ssize_t index = -1;
385         while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
386             const PidEntry *entry = sorted[index];
387             uid_t u = entry->getUid();
388             if ((uid != AID_ROOT) && (u != uid)) {
389                 continue;
390             }
391
392             if (!headerPrinted) {
393                 output.appendFormat("\n\n");
394                 android::String8 name("");
395                 if (uid == AID_ROOT) {
396                     name.appendFormat("Chattiest PIDs:");
397                 } else {
398                     name.appendFormat("Logging for this PID:");
399                 }
400                 android::String8 size("Size");
401                 android::String8 pruned("Pruned");
402                 format_line(output, name, size, pruned);
403
404                 name.setTo("  PID/UID   COMMAND LINE");
405                 size.setTo("BYTES");
406                 pruned.setTo("LINES");
407                 format_line(output, name, size, pruned);
408
409                 headerPrinted = true;
410             }
411
412             android::String8 name("");
413             name.appendFormat("%5u/%u", entry->getKey(), u);
414             const char *n = entry->getName();
415             if (n) {
416                 name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
417             } else {
418                 char *un = uidToName(u);
419                 if (un) {
420                     name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
421                     free(un);
422                 }
423             }
424
425             android::String8 size("");
426             size.appendFormat("%zu", entry->getSizes());
427
428             android::String8 pruned("");
429             size_t dropped = entry->getDropped();
430             if (dropped) {
431                 pruned.appendFormat("%zu", dropped);
432             }
433
434             format_line(output, name, size, pruned);
435         }
436     }
437
438     *buf = strdup(output.string());
439 }
440
441 namespace android {
442
443 uid_t pidToUid(pid_t pid) {
444     char buffer[512];
445     snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
446     FILE *fp = fopen(buffer, "r");
447     if (fp) {
448         while (fgets(buffer, sizeof(buffer), fp)) {
449             int uid;
450             if (sscanf(buffer, "Uid: %d", &uid) == 1) {
451                 fclose(fp);
452                 return uid;
453             }
454         }
455         fclose(fp);
456     }
457     return AID_LOGD; // associate this with the logger
458 }
459
460 }
461
462 uid_t LogStatistics::pidToUid(pid_t pid) {
463     uid_t uid;
464     android::hash_t hash = android::hash_type(pid);
465     ssize_t index = pidTable.find(-1, hash, pid);
466     if (index == -1) {
467         uid = android::pidToUid(pid);
468         PidEntry initEntry(pid, uid, android::pidToName(pid));
469         pidTable.add(hash, initEntry);
470     } else {
471         PidEntry &entry = pidTable.editEntryAt(index);
472         if (!entry.getName()) {
473             char *name = android::pidToName(pid);
474             if (name) {
475                 entry.setName(name);
476             }
477         }
478         uid = entry.getUid();
479     }
480     return uid;
481 }
482
483 // caller must free character string
484 char *LogStatistics::pidToName(pid_t pid) {
485     char *name;
486
487     android::hash_t hash = android::hash_type(pid);
488     ssize_t index = pidTable.find(-1, hash, pid);
489     if (index == -1) {
490         name = android::pidToName(pid);
491         PidEntry initEntry(pid, android::pidToUid(pid), name ? strdup(name) : NULL);
492         pidTable.add(hash, initEntry);
493     } else {
494         PidEntry &entry = pidTable.editEntryAt(index);
495         const char *n = entry.getName();
496         if (n) {
497             name = strdup(n);
498         } else {
499             name = android::pidToName(pid);
500             if (name) {
501                 entry.setName(strdup(name));
502             }
503         }
504     }
505
506     return name;
507 }