blob: 6b5ea4c23aa1d62bc368e291b460180253295979 [file] [log] [blame]
Mark Salyzync73e2b12013-11-22 07:54:30 -08001/*
The Android Open Source Project32315d42008-10-21 07:00:00 -07002**
Mark Salyzyn64bf6682013-12-18 12:59:01 -08003** Copyright 2006-2014, The Android Open Source Project
The Android Open Source Project32315d42008-10-21 07:00:00 -07004**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
Mark Salyzyn7ad91922016-03-08 16:18:26 -080018#ifndef __MINGW32__
19#define HAVE_STRSEP
20#endif
The Android Open Source Project32315d42008-10-21 07:00:00 -070021
Mark Salyzyn3a5219e2014-04-30 08:50:53 -070022#include <assert.h>
23#include <ctype.h>
24#include <errno.h>
Mark Salyzyn604679a2016-10-18 11:30:11 -070025#include <inttypes.h>
Mark Salyzyn7ad91922016-03-08 16:18:26 -080026#ifndef __MINGW32__
William Roberts6877c6c2016-04-08 12:13:17 -070027#include <pwd.h>
Mark Salyzyn7ad91922016-03-08 16:18:26 -080028#endif
Pierre Zurek507ea562010-10-17 22:39:37 +020029#include <stdbool.h>
Mark Salyzyn3a5219e2014-04-30 08:50:53 -070030#include <stdint.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
Pierre Zurek507ea562010-10-17 22:39:37 +020034#include <sys/param.h>
William Roberts6877c6c2016-04-08 12:13:17 -070035#include <sys/types.h>
Tom Cherry425317b2019-04-25 13:10:30 -070036#include <wchar.h>
The Android Open Source Project32315d42008-10-21 07:00:00 -070037
Mark Salyzyn4d0473f2015-08-31 15:53:41 -070038#include <cutils/list.h>
Mark Salyzyn8b8bfd22016-09-30 13:30:33 -070039#include <log/log.h>
Colin Crosse355ded2013-07-23 16:59:20 -070040#include <log/logprint.h>
The Android Open Source Project32315d42008-10-21 07:00:00 -070041
Mark Salyzyndd9094a2016-03-01 13:45:42 -080042#include "log_portability.h"
Mark Salyzyna5c22172016-03-10 08:25:33 -080043
Mark Salyzyn4d0473f2015-08-31 15:53:41 -070044#define MS_PER_NSEC 1000000
45#define US_PER_NSEC 1000
46
Mark Salyzyn7ad91922016-03-08 16:18:26 -080047#ifndef MIN
Mark Salyzyn6e315682017-03-09 08:09:43 -080048#define MIN(a, b) (((a) < (b)) ? (a) : (b))
Mark Salyzyn7ad91922016-03-08 16:18:26 -080049#endif
50
The Android Open Source Project32315d42008-10-21 07:00:00 -070051typedef struct FilterInfo_t {
Mark Salyzyn6e315682017-03-09 08:09:43 -080052 char* mTag;
53 android_LogPriority mPri;
54 struct FilterInfo_t* p_next;
The Android Open Source Project32315d42008-10-21 07:00:00 -070055} FilterInfo;
56
57struct AndroidLogFormat_t {
Mark Salyzyn6e315682017-03-09 08:09:43 -080058 android_LogPriority global_pri;
59 FilterInfo* filters;
60 AndroidLogPrintFormat format;
61 bool colored_output;
62 bool usec_time_output;
63 bool nsec_time_output;
64 bool printable_output;
65 bool year_output;
66 bool zone_output;
67 bool epoch_output;
68 bool monotonic_output;
69 bool uid_output;
70 bool descriptive_output;
The Android Open Source Project32315d42008-10-21 07:00:00 -070071};
72
Pierre Zurek507ea562010-10-17 22:39:37 +020073/*
Mark Salyzyn604679a2016-10-18 11:30:11 -070074 * API issues prevent us from exposing "descriptive" in AndroidLogFormat_t
75 * during android_log_processBinaryLogBuffer(), so we break layering.
76 */
77static bool descriptive_output = false;
78
79/*
Pierre Zurek507ea562010-10-17 22:39:37 +020080 * gnome-terminal color tags
81 * See http://misc.flogisoft.com/bash/tip_colors_and_formatting
82 * for ideas on how to set the forground color of the text for xterm.
83 * The color manipulation character stream is defined as:
84 * ESC [ 3 8 ; 5 ; <color#> m
85 */
Mark Salyzyn6e315682017-03-09 08:09:43 -080086#define ANDROID_COLOR_BLUE 75
Pierre Zurek507ea562010-10-17 22:39:37 +020087#define ANDROID_COLOR_DEFAULT 231
Mark Salyzyn6e315682017-03-09 08:09:43 -080088#define ANDROID_COLOR_GREEN 40
89#define ANDROID_COLOR_ORANGE 166
90#define ANDROID_COLOR_RED 196
91#define ANDROID_COLOR_YELLOW 226
Pierre Zurek507ea562010-10-17 22:39:37 +020092
Mark Salyzyn6e315682017-03-09 08:09:43 -080093static FilterInfo* filterinfo_new(const char* tag, android_LogPriority pri) {
94 FilterInfo* p_ret;
The Android Open Source Project32315d42008-10-21 07:00:00 -070095
Mark Salyzyn6e315682017-03-09 08:09:43 -080096 p_ret = (FilterInfo*)calloc(1, sizeof(FilterInfo));
97 p_ret->mTag = strdup(tag);
98 p_ret->mPri = pri;
The Android Open Source Project32315d42008-10-21 07:00:00 -070099
Mark Salyzyn6e315682017-03-09 08:09:43 -0800100 return p_ret;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700101}
102
Mark Salyzyn3a5219e2014-04-30 08:50:53 -0700103/* balance to above, filterinfo_free left unimplemented */
The Android Open Source Project32315d42008-10-21 07:00:00 -0700104
105/*
106 * Note: also accepts 0-9 priorities
107 * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
108 */
Mark Salyzyn6e315682017-03-09 08:09:43 -0800109static android_LogPriority filterCharToPri(char c) {
110 android_LogPriority pri;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700111
Mark Salyzyn6e315682017-03-09 08:09:43 -0800112 c = tolower(c);
The Android Open Source Project32315d42008-10-21 07:00:00 -0700113
Mark Salyzyn6e315682017-03-09 08:09:43 -0800114 if (c >= '0' && c <= '9') {
115 if (c >= ('0' + ANDROID_LOG_SILENT)) {
116 pri = ANDROID_LOG_VERBOSE;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700117 } else {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800118 pri = (android_LogPriority)(c - '0');
The Android Open Source Project32315d42008-10-21 07:00:00 -0700119 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800120 } else if (c == 'v') {
121 pri = ANDROID_LOG_VERBOSE;
122 } else if (c == 'd') {
123 pri = ANDROID_LOG_DEBUG;
124 } else if (c == 'i') {
125 pri = ANDROID_LOG_INFO;
126 } else if (c == 'w') {
127 pri = ANDROID_LOG_WARN;
128 } else if (c == 'e') {
129 pri = ANDROID_LOG_ERROR;
130 } else if (c == 'f') {
131 pri = ANDROID_LOG_FATAL;
132 } else if (c == 's') {
133 pri = ANDROID_LOG_SILENT;
134 } else if (c == '*') {
135 pri = ANDROID_LOG_DEFAULT;
136 } else {
137 pri = ANDROID_LOG_UNKNOWN;
138 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700139
Mark Salyzyn6e315682017-03-09 08:09:43 -0800140 return pri;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700141}
142
Mark Salyzyn6e315682017-03-09 08:09:43 -0800143static char filterPriToChar(android_LogPriority pri) {
144 switch (pri) {
145 /* clang-format off */
146 case ANDROID_LOG_VERBOSE: return 'V';
147 case ANDROID_LOG_DEBUG: return 'D';
148 case ANDROID_LOG_INFO: return 'I';
149 case ANDROID_LOG_WARN: return 'W';
150 case ANDROID_LOG_ERROR: return 'E';
151 case ANDROID_LOG_FATAL: return 'F';
152 case ANDROID_LOG_SILENT: return 'S';
The Android Open Source Project32315d42008-10-21 07:00:00 -0700153
Mark Salyzyn6e315682017-03-09 08:09:43 -0800154 case ANDROID_LOG_DEFAULT:
155 case ANDROID_LOG_UNKNOWN:
156 default: return '?';
Tom Cherryf623f022019-01-10 10:37:36 -0800157 /* clang-format on */
Mark Salyzyn6e315682017-03-09 08:09:43 -0800158 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700159}
160
Mark Salyzyn6e315682017-03-09 08:09:43 -0800161static int colorFromPri(android_LogPriority pri) {
162 switch (pri) {
163 /* clang-format off */
164 case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
165 case ANDROID_LOG_DEBUG: return ANDROID_COLOR_BLUE;
166 case ANDROID_LOG_INFO: return ANDROID_COLOR_GREEN;
167 case ANDROID_LOG_WARN: return ANDROID_COLOR_ORANGE;
168 case ANDROID_LOG_ERROR: return ANDROID_COLOR_RED;
169 case ANDROID_LOG_FATAL: return ANDROID_COLOR_RED;
170 case ANDROID_LOG_SILENT: return ANDROID_COLOR_DEFAULT;
Pierre Zurek507ea562010-10-17 22:39:37 +0200171
Mark Salyzyn6e315682017-03-09 08:09:43 -0800172 case ANDROID_LOG_DEFAULT:
173 case ANDROID_LOG_UNKNOWN:
174 default: return ANDROID_COLOR_DEFAULT;
Tom Cherryf623f022019-01-10 10:37:36 -0800175 /* clang-format on */
Mark Salyzyn6e315682017-03-09 08:09:43 -0800176 }
Pierre Zurek507ea562010-10-17 22:39:37 +0200177}
178
Tom Cherryf623f022019-01-10 10:37:36 -0800179static android_LogPriority filterPriForTag(AndroidLogFormat* p_format, const char* tag) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800180 FilterInfo* p_curFilter;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700181
Tom Cherryf623f022019-01-10 10:37:36 -0800182 for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800183 if (0 == strcmp(tag, p_curFilter->mTag)) {
184 if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
185 return p_format->global_pri;
186 } else {
187 return p_curFilter->mPri;
188 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700189 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800190 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700191
Mark Salyzyn6e315682017-03-09 08:09:43 -0800192 return p_format->global_pri;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700193}
194
The Android Open Source Project32315d42008-10-21 07:00:00 -0700195/**
196 * returns 1 if this log line should be printed based on its priority
197 * and tag, and 0 if it should not
198 */
Tom Cherry3d6a8782019-02-08 11:46:19 -0800199int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
200 android_LogPriority pri) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800201 return pri >= filterPriForTag(p_format, tag);
The Android Open Source Project32315d42008-10-21 07:00:00 -0700202}
203
Tom Cherry3d6a8782019-02-08 11:46:19 -0800204AndroidLogFormat* android_log_format_new() {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800205 AndroidLogFormat* p_ret;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700206
Tom Cherryf623f022019-01-10 10:37:36 -0800207 p_ret = static_cast<AndroidLogFormat*>(calloc(1, sizeof(AndroidLogFormat)));
The Android Open Source Project32315d42008-10-21 07:00:00 -0700208
Mark Salyzyn6e315682017-03-09 08:09:43 -0800209 p_ret->global_pri = ANDROID_LOG_VERBOSE;
210 p_ret->format = FORMAT_BRIEF;
211 p_ret->colored_output = false;
212 p_ret->usec_time_output = false;
213 p_ret->nsec_time_output = false;
214 p_ret->printable_output = false;
215 p_ret->year_output = false;
216 p_ret->zone_output = false;
217 p_ret->epoch_output = false;
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800218#ifdef __ANDROID__
Mark Salyzyn6e315682017-03-09 08:09:43 -0800219 p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800220#else
Mark Salyzyn6e315682017-03-09 08:09:43 -0800221 p_ret->monotonic_output = false;
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800222#endif
Mark Salyzyn6e315682017-03-09 08:09:43 -0800223 p_ret->uid_output = false;
224 p_ret->descriptive_output = false;
225 descriptive_output = false;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700226
Mark Salyzyn6e315682017-03-09 08:09:43 -0800227 return p_ret;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700228}
229
Mark Salyzyn4d0473f2015-08-31 15:53:41 -0700230static list_declare(convertHead);
231
Tom Cherry3d6a8782019-02-08 11:46:19 -0800232void android_log_format_free(AndroidLogFormat* p_format) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800233 FilterInfo *p_info, *p_info_old;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700234
Mark Salyzyn6e315682017-03-09 08:09:43 -0800235 p_info = p_format->filters;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700236
Mark Salyzyn6e315682017-03-09 08:09:43 -0800237 while (p_info != NULL) {
238 p_info_old = p_info;
239 p_info = p_info->p_next;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700240
Mark Salyzyn6e315682017-03-09 08:09:43 -0800241 free(p_info_old);
242 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700243
Mark Salyzyn6e315682017-03-09 08:09:43 -0800244 free(p_format);
Mark Salyzyn4d0473f2015-08-31 15:53:41 -0700245
Mark Salyzyn6e315682017-03-09 08:09:43 -0800246 /* Free conversion resource, can always be reconstructed */
247 while (!list_empty(&convertHead)) {
248 struct listnode* node = list_head(&convertHead);
249 list_remove(node);
Ting-Yuan Huangca6822f2017-08-15 17:01:33 -0700250 LOG_ALWAYS_FATAL_IF(node == list_head(&convertHead), "corrupted list");
Mark Salyzyn6e315682017-03-09 08:09:43 -0800251 free(node);
252 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700253}
254
Tom Cherry3d6a8782019-02-08 11:46:19 -0800255int android_log_setPrintFormat(AndroidLogFormat* p_format, AndroidLogPrintFormat format) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800256 switch (format) {
Mark Salyzyn1ebdb352015-05-15 09:01:58 -0700257 case FORMAT_MODIFIER_COLOR:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800258 p_format->colored_output = true;
259 return 0;
Mark Salyzyn1ebdb352015-05-15 09:01:58 -0700260 case FORMAT_MODIFIER_TIME_USEC:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800261 p_format->usec_time_output = true;
262 return 0;
Mark Salyzynf0050852017-01-30 09:16:09 -0800263 case FORMAT_MODIFIER_TIME_NSEC:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800264 p_format->nsec_time_output = true;
265 return 0;
Mark Salyzyn1ebdb352015-05-15 09:01:58 -0700266 case FORMAT_MODIFIER_PRINTABLE:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800267 p_format->printable_output = true;
268 return 0;
Mark Salyzyneb0456d2015-08-31 08:01:33 -0700269 case FORMAT_MODIFIER_YEAR:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800270 p_format->year_output = true;
271 return 0;
Mark Salyzyneb0456d2015-08-31 08:01:33 -0700272 case FORMAT_MODIFIER_ZONE:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800273 p_format->zone_output = !p_format->zone_output;
274 return 0;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -0700275 case FORMAT_MODIFIER_EPOCH:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800276 p_format->epoch_output = true;
277 return 0;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -0700278 case FORMAT_MODIFIER_MONOTONIC:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800279 p_format->monotonic_output = true;
280 return 0;
Mark Salyzynb71da912015-12-07 16:52:42 -0800281 case FORMAT_MODIFIER_UID:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800282 p_format->uid_output = true;
283 return 0;
Mark Salyzyn604679a2016-10-18 11:30:11 -0700284 case FORMAT_MODIFIER_DESCRIPT:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800285 p_format->descriptive_output = true;
286 descriptive_output = true;
287 return 0;
Mark Salyzyn1ebdb352015-05-15 09:01:58 -0700288 default:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800289 break;
290 }
291 p_format->format = format;
292 return 1;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700293}
294
Mark Salyzyneb0456d2015-08-31 08:01:33 -0700295static const char tz[] = "TZ";
296static const char utc[] = "UTC";
297
The Android Open Source Project32315d42008-10-21 07:00:00 -0700298/**
299 * Returns FORMAT_OFF on invalid string
300 */
Tom Cherry3d6a8782019-02-08 11:46:19 -0800301AndroidLogPrintFormat android_log_formatFromString(const char* formatString) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800302 static AndroidLogPrintFormat format;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700303
Mark Salyzyn6e315682017-03-09 08:09:43 -0800304 /* clang-format off */
305 if (!strcmp(formatString, "brief")) format = FORMAT_BRIEF;
306 else if (!strcmp(formatString, "process")) format = FORMAT_PROCESS;
307 else if (!strcmp(formatString, "tag")) format = FORMAT_TAG;
308 else if (!strcmp(formatString, "thread")) format = FORMAT_THREAD;
309 else if (!strcmp(formatString, "raw")) format = FORMAT_RAW;
310 else if (!strcmp(formatString, "time")) format = FORMAT_TIME;
311 else if (!strcmp(formatString, "threadtime")) format = FORMAT_THREADTIME;
312 else if (!strcmp(formatString, "long")) format = FORMAT_LONG;
313 else if (!strcmp(formatString, "color")) format = FORMAT_MODIFIER_COLOR;
314 else if (!strcmp(formatString, "colour")) format = FORMAT_MODIFIER_COLOR;
315 else if (!strcmp(formatString, "usec")) format = FORMAT_MODIFIER_TIME_USEC;
316 else if (!strcmp(formatString, "nsec")) format = FORMAT_MODIFIER_TIME_NSEC;
317 else if (!strcmp(formatString, "printable")) format = FORMAT_MODIFIER_PRINTABLE;
318 else if (!strcmp(formatString, "year")) format = FORMAT_MODIFIER_YEAR;
319 else if (!strcmp(formatString, "zone")) format = FORMAT_MODIFIER_ZONE;
320 else if (!strcmp(formatString, "epoch")) format = FORMAT_MODIFIER_EPOCH;
321 else if (!strcmp(formatString, "monotonic")) format = FORMAT_MODIFIER_MONOTONIC;
322 else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
323 else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
Tom Cherryf623f022019-01-10 10:37:36 -0800324 /* clang-format on */
Mark Salyzyn07ff2122016-11-10 10:24:44 -0800325
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800326#ifndef __MINGW32__
Mark Salyzyn6e315682017-03-09 08:09:43 -0800327 else {
328 extern char* tzname[2];
329 static const char gmt[] = "GMT";
330 char* cp = getenv(tz);
331 if (cp) {
332 cp = strdup(cp);
Mark Salyzyneb0456d2015-08-31 08:01:33 -0700333 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800334 setenv(tz, formatString, 1);
335 /*
336 * Run tzset here to determine if the timezone is legitimate. If the
337 * zone is GMT, check if that is what was asked for, if not then
338 * did not match any on the system; report an error to caller.
339 */
340 tzset();
341 if (!tzname[0] ||
Tom Cherryf623f022019-01-10 10:37:36 -0800342 ((!strcmp(tzname[0], utc) || !strcmp(tzname[0], gmt)) /* error? */
343 && strcasecmp(formatString, utc) && strcasecmp(formatString, gmt))) { /* ok */
Mark Salyzyn6e315682017-03-09 08:09:43 -0800344 if (cp) {
345 setenv(tz, cp, 1);
346 } else {
347 unsetenv(tz);
348 }
349 tzset();
350 format = FORMAT_OFF;
351 } else {
352 format = FORMAT_MODIFIER_ZONE;
353 }
354 free(cp);
355 }
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800356#endif
The Android Open Source Project32315d42008-10-21 07:00:00 -0700357
Mark Salyzyn6e315682017-03-09 08:09:43 -0800358 return format;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700359}
360
361/**
362 * filterExpression: a single filter expression
363 * eg "AT:d"
364 *
365 * returns 0 on success and -1 on invalid expression
366 *
367 * Assumes single threaded execution
368 */
369
Tom Cherry3d6a8782019-02-08 11:46:19 -0800370int android_log_addFilterRule(AndroidLogFormat* p_format, const char* filterExpression) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800371 size_t tagNameLength;
372 android_LogPriority pri = ANDROID_LOG_DEFAULT;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700373
Mark Salyzyn6e315682017-03-09 08:09:43 -0800374 tagNameLength = strcspn(filterExpression, ":");
The Android Open Source Project32315d42008-10-21 07:00:00 -0700375
Mark Salyzyn6e315682017-03-09 08:09:43 -0800376 if (tagNameLength == 0) {
377 goto error;
378 }
379
380 if (filterExpression[tagNameLength] == ':') {
381 pri = filterCharToPri(filterExpression[tagNameLength + 1]);
382
383 if (pri == ANDROID_LOG_UNKNOWN) {
384 goto error;
385 }
386 }
387
388 if (0 == strncmp("*", filterExpression, tagNameLength)) {
389 /*
390 * This filter expression refers to the global filter
391 * The default level for this is DEBUG if the priority
392 * is unspecified
393 */
394 if (pri == ANDROID_LOG_DEFAULT) {
395 pri = ANDROID_LOG_DEBUG;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700396 }
397
Mark Salyzyn6e315682017-03-09 08:09:43 -0800398 p_format->global_pri = pri;
399 } else {
400 /*
401 * for filter expressions that don't refer to the global
402 * filter, the default is verbose if the priority is unspecified
403 */
404 if (pri == ANDROID_LOG_DEFAULT) {
405 pri = ANDROID_LOG_VERBOSE;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700406 }
407
Mark Salyzyn6e315682017-03-09 08:09:43 -0800408 char* tagName;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700409
Mark Salyzyn1ebdb352015-05-15 09:01:58 -0700410/*
411 * Presently HAVE_STRNDUP is never defined, so the second case is always taken
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800412 * Darwin doesn't have strndup, everything else does
Mark Salyzyn1ebdb352015-05-15 09:01:58 -0700413 */
The Android Open Source Project32315d42008-10-21 07:00:00 -0700414#ifdef HAVE_STRNDUP
Mark Salyzyn6e315682017-03-09 08:09:43 -0800415 tagName = strndup(filterExpression, tagNameLength);
The Android Open Source Project32315d42008-10-21 07:00:00 -0700416#else
Mark Salyzyn6e315682017-03-09 08:09:43 -0800417 /* a few extra bytes copied... */
418 tagName = strdup(filterExpression);
419 tagName[tagNameLength] = '\0';
The Android Open Source Project32315d42008-10-21 07:00:00 -0700420#endif /*HAVE_STRNDUP*/
421
Mark Salyzyn6e315682017-03-09 08:09:43 -0800422 FilterInfo* p_fi = filterinfo_new(tagName, pri);
423 free(tagName);
The Android Open Source Project32315d42008-10-21 07:00:00 -0700424
Mark Salyzyn6e315682017-03-09 08:09:43 -0800425 p_fi->p_next = p_format->filters;
426 p_format->filters = p_fi;
427 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700428
Mark Salyzyn6e315682017-03-09 08:09:43 -0800429 return 0;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700430error:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800431 return -1;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700432}
433
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800434#ifndef HAVE_STRSEP
435/* KISS replacement helper for below */
Mark Salyzyn6e315682017-03-09 08:09:43 -0800436static char* strsep(char** stringp, const char* delim) {
437 char* token;
438 char* ret = *stringp;
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800439
Mark Salyzyn6e315682017-03-09 08:09:43 -0800440 if (!ret || !*ret) {
441 return NULL;
442 }
443 token = strpbrk(ret, delim);
444 if (token) {
445 *token = '\0';
446 ++token;
447 } else {
448 token = ret + strlen(ret);
449 }
450 *stringp = token;
451 return ret;
Mark Salyzyn7ad91922016-03-08 16:18:26 -0800452}
453#endif
The Android Open Source Project32315d42008-10-21 07:00:00 -0700454
455/**
456 * filterString: a comma/whitespace-separated set of filter expressions
457 *
458 * eg "AT:d *:i"
459 *
460 * returns 0 on success and -1 on invalid expression
461 *
462 * Assumes single threaded execution
463 *
464 */
Tom Cherry3d6a8782019-02-08 11:46:19 -0800465int android_log_addFilterString(AndroidLogFormat* p_format, const char* filterString) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800466 char* filterStringCopy = strdup(filterString);
467 char* p_cur = filterStringCopy;
468 char* p_ret;
469 int err;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700470
Mark Salyzyn6e315682017-03-09 08:09:43 -0800471 /* Yes, I'm using strsep */
472 while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
473 /* ignore whitespace-only entries */
474 if (p_ret[0] != '\0') {
475 err = android_log_addFilterRule(p_format, p_ret);
The Android Open Source Project32315d42008-10-21 07:00:00 -0700476
Mark Salyzyn6e315682017-03-09 08:09:43 -0800477 if (err < 0) {
478 goto error;
479 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700480 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800481 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700482
Mark Salyzyn6e315682017-03-09 08:09:43 -0800483 free(filterStringCopy);
484 return 0;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700485error:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800486 free(filterStringCopy);
487 return -1;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700488}
489
The Android Open Source Project32315d42008-10-21 07:00:00 -0700490/**
491 * Splits a wire-format buffer into an AndroidLogEntry
492 * entry allocated by caller. Pointers will point directly into buf
493 *
494 * Returns 0 on success and -1 on invalid wire format (entry will be
495 * in unspecified state)
496 */
Tom Cherry3d6a8782019-02-08 11:46:19 -0800497int android_log_processLogBuffer(struct logger_entry* buf, AndroidLogEntry* entry) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800498 entry->message = NULL;
499 entry->messageLen = 0;
Mark Salyzyned47c7b2017-03-02 07:47:21 -0800500
Mark Salyzyn6e315682017-03-09 08:09:43 -0800501 entry->tv_sec = buf->sec;
502 entry->tv_nsec = buf->nsec;
503 entry->uid = -1;
504 entry->pid = buf->pid;
505 entry->tid = buf->tid;
Kenny Rootfa7c09a2011-09-30 17:10:14 -0700506
Mark Salyzyn6e315682017-03-09 08:09:43 -0800507 /*
508 * format: <priority:1><tag:N>\0<message:N>\0
509 *
510 * tag str
511 * starts at buf->msg+1
512 * msg
513 * starts at buf->msg+1+len(tag)+1
514 *
515 * The message may have been truncated by the kernel log driver.
516 * When that happens, we must null-terminate the message ourselves.
517 */
518 if (buf->len < 3) {
Kenny Rootfa7c09a2011-09-30 17:10:14 -0700519 /*
Mark Salyzyn6e315682017-03-09 08:09:43 -0800520 * An well-formed entry must consist of at least a priority
521 * and two null characters
Kenny Rootfa7c09a2011-09-30 17:10:14 -0700522 */
Mark Salyzyn6e315682017-03-09 08:09:43 -0800523 fprintf(stderr, "+++ LOG: entry too small\n");
524 return -1;
525 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700526
Mark Salyzyn6e315682017-03-09 08:09:43 -0800527 int msgStart = -1;
528 int msgEnd = -1;
Jeff Sharkey059ac702011-10-26 18:40:39 -0700529
Mark Salyzyn6e315682017-03-09 08:09:43 -0800530 int i;
531 char* msg = buf->msg;
532 struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
533 if (buf2->hdr_size) {
534 if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
535 (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
536 fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
537 return -1;
Mark Salyzyn64bf6682013-12-18 12:59:01 -0800538 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800539 msg = ((char*)buf2) + buf2->hdr_size;
540 if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
541 entry->uid = ((struct logger_entry_v4*)buf)->uid;
542 }
543 }
544 for (i = 1; i < buf->len; i++) {
545 if (msg[i] == '\0') {
546 if (msgStart == -1) {
547 msgStart = i + 1;
548 } else {
549 msgEnd = i;
550 break;
551 }
552 }
553 }
554
555 if (msgStart == -1) {
556 /* +++ LOG: malformed log message, DYB */
Nick Kralevich2f2789a2011-10-18 15:23:33 -0700557 for (i = 1; i < buf->len; i++) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800558 /* odd characters in tag? */
559 if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
560 msg[i] = '\0';
561 msgStart = i + 1;
562 break;
563 }
Nick Kralevich2f2789a2011-10-18 15:23:33 -0700564 }
Jeff Sharkey059ac702011-10-26 18:40:39 -0700565 if (msgStart == -1) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800566 msgStart = buf->len - 1; /* All tag, no message, print truncates */
Nick Kralevich32be08b2011-10-17 10:45:03 -0700567 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800568 }
569 if (msgEnd == -1) {
570 /* incoming message not null-terminated; force it */
571 msgEnd = buf->len - 1; /* may result in msgEnd < msgStart */
572 msg[msgEnd] = '\0';
573 }
Jeff Sharkey059ac702011-10-26 18:40:39 -0700574
Tom Cherryf623f022019-01-10 10:37:36 -0800575 entry->priority = static_cast<android_LogPriority>(msg[0]);
Mark Salyzyn6e315682017-03-09 08:09:43 -0800576 entry->tag = msg + 1;
577 entry->tagLen = msgStart - 1;
578 entry->message = msg + msgStart;
579 entry->messageLen = (msgEnd < msgStart) ? 0 : (msgEnd - msgStart);
Nick Kralevich32be08b2011-10-17 10:45:03 -0700580
Mark Salyzyn6e315682017-03-09 08:09:43 -0800581 return 0;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700582}
583
584/*
Mark Salyzynbd8b8902015-03-06 20:42:57 +0000585 * Extract a 4-byte value from a byte stream.
586 */
Mark Salyzyn6e315682017-03-09 08:09:43 -0800587static inline uint32_t get4LE(const uint8_t* src) {
588 return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
Mark Salyzynbd8b8902015-03-06 20:42:57 +0000589}
590
591/*
592 * Extract an 8-byte value from a byte stream.
593 */
Mark Salyzyn6e315682017-03-09 08:09:43 -0800594static inline uint64_t get8LE(const uint8_t* src) {
595 uint32_t low, high;
Mark Salyzynbd8b8902015-03-06 20:42:57 +0000596
Mark Salyzyn6e315682017-03-09 08:09:43 -0800597 low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
598 high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
599 return ((uint64_t)high << 32) | (uint64_t)low;
Mark Salyzynbd8b8902015-03-06 20:42:57 +0000600}
601
Mark Salyzyn604679a2016-10-18 11:30:11 -0700602static bool findChar(const char** cp, size_t* len, int c) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800603 while ((*len) && isspace(*(*cp))) {
604 ++(*cp);
605 --(*len);
606 }
607 if (c == INT_MAX) return *len;
608 if ((*len) && (*(*cp) == c)) {
609 ++(*cp);
610 --(*len);
611 return true;
612 }
613 return false;
Mark Salyzyn604679a2016-10-18 11:30:11 -0700614}
Mark Salyzynbd8b8902015-03-06 20:42:57 +0000615
616/*
The Android Open Source Project32315d42008-10-21 07:00:00 -0700617 * Recursively convert binary log data to printable form.
618 *
619 * This needs to be recursive because you can have lists of lists.
620 *
621 * If we run out of room, we stop processing immediately. It's important
622 * for us to check for space on every output element to avoid producing
623 * garbled output.
624 *
625 * Returns 0 on success, 1 on buffer full, -1 on failure.
626 */
Mark Salyzyn604679a2016-10-18 11:30:11 -0700627enum objectType {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800628 TYPE_OBJECTS = '1',
629 TYPE_BYTES = '2',
630 TYPE_MILLISECONDS = '3',
631 TYPE_ALLOCATIONS = '4',
632 TYPE_ID = '5',
Mark Salyzyn07ff2122016-11-10 10:24:44 -0800633 TYPE_PERCENT = '6',
634 TYPE_MONOTONIC = 's'
Mark Salyzyn604679a2016-10-18 11:30:11 -0700635};
636
Tom Cherryf623f022019-01-10 10:37:36 -0800637static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen,
638 char** pOutBuf, size_t* pOutBufLen, const char** fmtStr,
Mark Salyzyn6e315682017-03-09 08:09:43 -0800639 size_t* fmtLen) {
640 const unsigned char* eventData = *pEventData;
641 size_t eventDataLen = *pEventDataLen;
642 char* outBuf = *pOutBuf;
643 char* outBufSave = outBuf;
644 size_t outBufLen = *pOutBufLen;
645 size_t outBufLenSave = outBufLen;
646 unsigned char type;
Mark Salyzyn07ff2122016-11-10 10:24:44 -0800647 size_t outCount = 0;
Mark Salyzyn6e315682017-03-09 08:09:43 -0800648 int result = 0;
649 const char* cp;
650 size_t len;
651 int64_t lval;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700652
Mark Salyzyn6e315682017-03-09 08:09:43 -0800653 if (eventDataLen < 1) return -1;
Mark Salyzyn555064b2016-11-11 14:41:30 -0800654
Mark Salyzyn6e315682017-03-09 08:09:43 -0800655 type = *eventData++;
656 eventDataLen--;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700657
Mark Salyzyn6e315682017-03-09 08:09:43 -0800658 cp = NULL;
659 len = 0;
660 if (fmtStr && *fmtStr && fmtLen && *fmtLen && **fmtStr) {
661 cp = *fmtStr;
662 len = *fmtLen;
663 }
664 /*
665 * event.logtag format specification:
666 *
667 * Optionally, after the tag names can be put a description for the value(s)
668 * of the tag. Description are in the format
669 * (<name>|data type[|data unit])
670 * Multiple values are separated by commas.
671 *
672 * The data type is a number from the following values:
673 * 1: int
674 * 2: long
675 * 3: string
676 * 4: list
677 * 5: float
678 *
679 * The data unit is a number taken from the following list:
680 * 1: Number of objects
681 * 2: Number of bytes
682 * 3: Number of milliseconds
683 * 4: Number of allocations
684 * 5: Id
685 * 6: Percent
Mark Salyzyn07ff2122016-11-10 10:24:44 -0800686 * s: Number of seconds (monotonic time)
Mark Salyzyn6e315682017-03-09 08:09:43 -0800687 * Default value for data of type int/long is 2 (bytes).
688 */
689 if (!cp || !findChar(&cp, &len, '(')) {
Mark Salyzyn604679a2016-10-18 11:30:11 -0700690 len = 0;
Mark Salyzyn6e315682017-03-09 08:09:43 -0800691 } else {
692 char* outBufLastSpace = NULL;
693
694 findChar(&cp, &len, INT_MAX);
695 while (len && *cp && (*cp != '|') && (*cp != ')')) {
696 if (outBufLen <= 0) {
697 /* halt output */
698 goto no_room;
699 }
700 outBufLastSpace = isspace(*cp) ? outBuf : NULL;
701 *outBuf = *cp;
702 ++outBuf;
703 ++cp;
704 --outBufLen;
705 --len;
Mark Salyzyn604679a2016-10-18 11:30:11 -0700706 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800707 if (outBufLastSpace) {
708 outBufLen += outBuf - outBufLastSpace;
709 outBuf = outBufLastSpace;
710 }
711 if (outBufLen <= 0) {
712 /* halt output */
713 goto no_room;
714 }
715 if (outBufSave != outBuf) {
716 *outBuf = '=';
717 ++outBuf;
718 --outBufLen;
719 }
720
721 if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
Tom Cherryf623f022019-01-10 10:37:36 -0800722 static const unsigned char typeTable[] = {EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING,
723 EVENT_TYPE_LIST, EVENT_TYPE_FLOAT};
Mark Salyzyn6e315682017-03-09 08:09:43 -0800724
Tom Cherryf623f022019-01-10 10:37:36 -0800725 if ((*cp >= '1') && (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
Mark Salyzyn6e315682017-03-09 08:09:43 -0800726 (type != typeTable[(size_t)(*cp - '1')]))
Mark Salyzyn604679a2016-10-18 11:30:11 -0700727 len = 0;
Mark Salyzyn604679a2016-10-18 11:30:11 -0700728
Mark Salyzyn6e315682017-03-09 08:09:43 -0800729 if (len) {
730 ++cp;
731 --len;
732 } else {
733 /* reset the format */
734 outBuf = outBufSave;
735 outBufLen = outBufLenSave;
736 }
Mark Salyzyn604679a2016-10-18 11:30:11 -0700737 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800738 }
739 outCount = 0;
740 lval = 0;
741 switch (type) {
The Android Open Source Project32315d42008-10-21 07:00:00 -0700742 case EVENT_TYPE_INT:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800743 /* 32-bit signed int */
744 {
745 int32_t ival;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700746
Mark Salyzyn6e315682017-03-09 08:09:43 -0800747 if (eventDataLen < 4) return -1;
748 ival = get4LE(eventData);
749 eventData += 4;
750 eventDataLen -= 4;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700751
Mark Salyzyn6e315682017-03-09 08:09:43 -0800752 lval = ival;
753 }
754 goto pr_lval;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700755 case EVENT_TYPE_LONG:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800756 /* 64-bit signed long */
757 if (eventDataLen < 8) return -1;
758 lval = get8LE(eventData);
759 eventData += 8;
760 eventDataLen -= 8;
Mark Salyzyn604679a2016-10-18 11:30:11 -0700761 pr_lval:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800762 outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
763 if (outCount < outBufLen) {
764 outBuf += outCount;
765 outBufLen -= outCount;
766 } else {
767 /* halt output */
768 goto no_room;
769 }
770 break;
Jeff Brown9cac7fc2015-04-28 12:47:02 -0700771 case EVENT_TYPE_FLOAT:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800772 /* float */
773 {
774 uint32_t ival;
775 float fval;
Jeff Brown9cac7fc2015-04-28 12:47:02 -0700776
Mark Salyzyn6e315682017-03-09 08:09:43 -0800777 if (eventDataLen < 4) return -1;
778 ival = get4LE(eventData);
779 fval = *(float*)&ival;
780 eventData += 4;
781 eventDataLen -= 4;
Jeff Brown9cac7fc2015-04-28 12:47:02 -0700782
Mark Salyzyn6e315682017-03-09 08:09:43 -0800783 outCount = snprintf(outBuf, outBufLen, "%f", fval);
784 if (outCount < outBufLen) {
785 outBuf += outCount;
786 outBufLen -= outCount;
787 } else {
788 /* halt output */
789 goto no_room;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700790 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800791 }
792 break;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700793 case EVENT_TYPE_STRING:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800794 /* UTF-8 chars, not NULL-terminated */
795 {
796 unsigned int strLen;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700797
Mark Salyzyn6e315682017-03-09 08:09:43 -0800798 if (eventDataLen < 4) return -1;
799 strLen = get4LE(eventData);
800 eventData += 4;
801 eventDataLen -= 4;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700802
Mark Salyzyn6e315682017-03-09 08:09:43 -0800803 if (eventDataLen < strLen) {
804 result = -1; /* mark truncated */
805 strLen = eventDataLen;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700806 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700807
Mark Salyzyn6e315682017-03-09 08:09:43 -0800808 if (cp && (strLen == 0)) {
809 /* reset the format if no content */
810 outBuf = outBufSave;
811 outBufLen = outBufLenSave;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700812 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800813 if (strLen < outBufLen) {
814 memcpy(outBuf, eventData, strLen);
815 outBuf += strLen;
816 outBufLen -= strLen;
817 } else {
818 if (outBufLen > 0) {
819 /* copy what we can */
820 memcpy(outBuf, eventData, outBufLen);
821 outBuf += outBufLen;
822 outBufLen -= outBufLen;
823 }
824 if (!result) result = 1; /* if not truncated, return no room */
825 }
826 eventData += strLen;
827 eventDataLen -= strLen;
828 if (result != 0) goto bail;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700829 break;
Mark Salyzyn6e315682017-03-09 08:09:43 -0800830 }
831 case EVENT_TYPE_LIST:
832 /* N items, all different types */
833 {
834 unsigned char count;
835 int i;
836
837 if (eventDataLen < 1) return -1;
838
839 count = *eventData++;
840 eventDataLen--;
841
842 if (outBufLen <= 0) goto no_room;
843
844 *outBuf++ = '[';
845 outBufLen--;
846
847 for (i = 0; i < count; i++) {
Tom Cherryf623f022019-01-10 10:37:36 -0800848 result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
849 fmtStr, fmtLen);
Mark Salyzyn6e315682017-03-09 08:09:43 -0800850 if (result != 0) goto bail;
851
852 if (i < (count - 1)) {
853 if (outBufLen <= 0) goto no_room;
854 *outBuf++ = ',';
855 outBufLen--;
856 }
Mark Salyzyn604679a2016-10-18 11:30:11 -0700857 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800858
859 if (outBufLen <= 0) goto no_room;
860
861 *outBuf++ = ']';
862 outBufLen--;
863 }
864 break;
865 default:
866 fprintf(stderr, "Unknown binary event type %d\n", type);
867 return -1;
868 }
869 if (cp && len) {
870 if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
871 switch (*cp) {
872 case TYPE_OBJECTS:
873 outCount = 0;
874 /* outCount = snprintf(outBuf, outBufLen, " objects"); */
875 break;
876 case TYPE_BYTES:
877 if ((lval != 0) && ((lval % 1024) == 0)) {
878 /* repaint with multiplier */
Tom Cherryf623f022019-01-10 10:37:36 -0800879 static const char suffixTable[] = {'K', 'M', 'G', 'T'};
Mark Salyzyn6e315682017-03-09 08:09:43 -0800880 size_t idx = 0;
881 outBuf -= outCount;
882 outBufLen += outCount;
883 do {
884 lval /= 1024;
885 if ((lval % 1024) != 0) break;
Tom Cherryf623f022019-01-10 10:37:36 -0800886 } while (++idx < ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
887 outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval, suffixTable[idx]);
Mark Salyzyn6e315682017-03-09 08:09:43 -0800888 } else {
889 outCount = snprintf(outBuf, outBufLen, "B");
890 }
891 break;
892 case TYPE_MILLISECONDS:
Tom Cherryf623f022019-01-10 10:37:36 -0800893 if (((lval <= -1000) || (1000 <= lval)) && (outBufLen || (outBuf[-1] == '0'))) {
Mark Salyzyn6e315682017-03-09 08:09:43 -0800894 /* repaint as (fractional) seconds, possibly saving space */
895 if (outBufLen) outBuf[0] = outBuf[-1];
896 outBuf[-1] = outBuf[-2];
897 outBuf[-2] = outBuf[-3];
898 outBuf[-3] = '.';
899 while ((outBufLen == 0) || (*outBuf == '0')) {
900 --outBuf;
901 ++outBufLen;
902 }
903 if (*outBuf != '.') {
904 ++outBuf;
905 --outBufLen;
906 }
907 outCount = snprintf(outBuf, outBufLen, "s");
908 } else {
909 outCount = snprintf(outBuf, outBufLen, "ms");
910 }
911 break;
Mark Salyzyn07ff2122016-11-10 10:24:44 -0800912 case TYPE_MONOTONIC: {
913 static const uint64_t minute = 60;
914 static const uint64_t hour = 60 * minute;
915 static const uint64_t day = 24 * hour;
916
917 /* Repaint as unsigned seconds, minutes, hours ... */
918 outBuf -= outCount;
919 outBufLen += outCount;
920 uint64_t val = lval;
921 if (val >= day) {
922 outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
923 if (outCount >= outBufLen) break;
924 outBuf += outCount;
925 outBufLen -= outCount;
926 val = (val % day) + day;
927 }
928 if (val >= minute) {
929 if (val >= hour) {
Tom Cherryf623f022019-01-10 10:37:36 -0800930 outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":", (val / hour) % (day / hour));
Mark Salyzyn07ff2122016-11-10 10:24:44 -0800931 if (outCount >= outBufLen) break;
932 outBuf += outCount;
933 outBufLen -= outCount;
934 }
935 outCount =
Tom Cherryf623f022019-01-10 10:37:36 -0800936 snprintf(outBuf, outBufLen, (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
Mark Salyzyn07ff2122016-11-10 10:24:44 -0800937 (val / minute) % (hour / minute));
938 if (outCount >= outBufLen) break;
939 outBuf += outCount;
940 outBufLen -= outCount;
941 }
Tom Cherryf623f022019-01-10 10:37:36 -0800942 outCount = snprintf(outBuf, outBufLen, (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
Mark Salyzyn07ff2122016-11-10 10:24:44 -0800943 val % minute);
944 } break;
Mark Salyzyn6e315682017-03-09 08:09:43 -0800945 case TYPE_ALLOCATIONS:
946 outCount = 0;
947 /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
948 break;
949 case TYPE_ID:
950 outCount = 0;
951 break;
952 case TYPE_PERCENT:
953 outCount = snprintf(outBuf, outBufLen, "%%");
954 break;
955 default: /* ? */
956 outCount = 0;
957 break;
958 }
959 ++cp;
960 --len;
961 if (outCount < outBufLen) {
962 outBuf += outCount;
963 outBufLen -= outCount;
964 } else if (outCount) {
965 /* halt output */
966 goto no_room;
967 }
Mark Salyzyn604679a2016-10-18 11:30:11 -0700968 }
Mark Salyzyn6e315682017-03-09 08:09:43 -0800969 if (!findChar(&cp, &len, ')')) len = 0;
970 if (!findChar(&cp, &len, ',')) len = 0;
971 }
The Android Open Source Project32315d42008-10-21 07:00:00 -0700972
973bail:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800974 *pEventData = eventData;
975 *pEventDataLen = eventDataLen;
976 *pOutBuf = outBuf;
977 *pOutBufLen = outBufLen;
978 if (cp) {
979 *fmtStr = cp;
980 *fmtLen = len;
981 }
982 return result;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700983
984no_room:
Mark Salyzyn6e315682017-03-09 08:09:43 -0800985 result = 1;
986 goto bail;
The Android Open Source Project32315d42008-10-21 07:00:00 -0700987}
988
989/**
990 * Convert a binary log entry to ASCII form.
991 *
992 * For convenience we mimic the processLogBuffer API. There is no
993 * pre-defined output length for the binary data, since we're free to format
994 * it however we choose, which means we can't really use a fixed-size buffer
995 * here.
996 */
Tom Cherry3d6a8782019-02-08 11:46:19 -0800997int android_log_processBinaryLogBuffer(
Mark Salyzyn6e315682017-03-09 08:09:43 -0800998 struct logger_entry* buf, AndroidLogEntry* entry,
Tom Cherryf623f022019-01-10 10:37:36 -0800999 [[maybe_unused]] const EventTagMap* map, /* only on !__ANDROID__ */
Mark Salyzyn6e315682017-03-09 08:09:43 -08001000 char* messageBuf, int messageBufLen) {
1001 size_t inCount;
1002 uint32_t tagIndex;
1003 const unsigned char* eventData;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001004
Mark Salyzyn6e315682017-03-09 08:09:43 -08001005 entry->message = NULL;
1006 entry->messageLen = 0;
Mark Salyzyned47c7b2017-03-02 07:47:21 -08001007
Mark Salyzyn6e315682017-03-09 08:09:43 -08001008 entry->tv_sec = buf->sec;
1009 entry->tv_nsec = buf->nsec;
1010 entry->priority = ANDROID_LOG_INFO;
1011 entry->uid = -1;
1012 entry->pid = buf->pid;
1013 entry->tid = buf->tid;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001014
Mark Salyzyn6e315682017-03-09 08:09:43 -08001015 /*
1016 * Pull the tag out, fill in some additional details based on incoming
1017 * buffer version (v3 adds lid, v4 adds uid).
1018 */
1019 eventData = (const unsigned char*)buf->msg;
1020 struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
1021 if (buf2->hdr_size) {
1022 if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
1023 (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
1024 fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
1025 return -1;
1026 }
1027 eventData = ((unsigned char*)buf2) + buf2->hdr_size;
1028 if ((buf2->hdr_size >= sizeof(struct logger_entry_v3)) &&
1029 (((struct logger_entry_v3*)buf)->lid == LOG_ID_SECURITY)) {
1030 entry->priority = ANDROID_LOG_WARN;
1031 }
1032 if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
1033 entry->uid = ((struct logger_entry_v4*)buf)->uid;
1034 }
1035 }
1036 inCount = buf->len;
1037 if (inCount < 4) return -1;
1038 tagIndex = get4LE(eventData);
1039 eventData += 4;
1040 inCount -= 4;
1041
1042 entry->tagLen = 0;
1043 entry->tag = NULL;
1044#ifdef __ANDROID__
1045 if (map != NULL) {
1046 entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex);
1047 }
1048#endif
1049
1050 /*
1051 * If we don't have a map, or didn't find the tag number in the map,
1052 * stuff a generated tag value into the start of the output buffer and
1053 * shift the buffer pointers down.
1054 */
1055 if (entry->tag == NULL) {
1056 size_t tagLen;
1057
1058 tagLen = snprintf(messageBuf, messageBufLen, "[%" PRIu32 "]", tagIndex);
1059 if (tagLen >= (size_t)messageBufLen) {
1060 tagLen = messageBufLen - 1;
1061 }
1062 entry->tag = messageBuf;
1063 entry->tagLen = tagLen;
1064 messageBuf += tagLen + 1;
1065 messageBufLen -= tagLen + 1;
1066 }
1067
1068 /*
1069 * Format the event log data into the buffer.
1070 */
1071 const char* fmtStr = NULL;
1072 size_t fmtLen = 0;
1073#ifdef __ANDROID__
1074 if (descriptive_output && map) {
1075 fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
1076 }
1077#endif
1078
1079 char* outBuf = messageBuf;
1080 size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
1081 int result = 0;
1082
1083 if ((inCount > 0) || fmtLen) {
Tom Cherryf623f022019-01-10 10:37:36 -08001084 result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, &fmtStr,
1085 &fmtLen);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001086 }
1087 if ((result == 1) && fmtStr) {
1088 /* We overflowed :-(, let's repaint the line w/o format dressings */
Mark Salyzyned47c7b2017-03-02 07:47:21 -08001089 eventData = (const unsigned char*)buf->msg;
Mark Salyzyn64bf6682013-12-18 12:59:01 -08001090 if (buf2->hdr_size) {
Mark Salyzyn6e315682017-03-09 08:09:43 -08001091 eventData = ((unsigned char*)buf2) + buf2->hdr_size;
Mark Salyzyn64bf6682013-12-18 12:59:01 -08001092 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001093 eventData += 4;
Mark Salyzyn6e315682017-03-09 08:09:43 -08001094 outBuf = messageBuf;
1095 outRemaining = messageBufLen - 1;
Tom Cherryf623f022019-01-10 10:37:36 -08001096 result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, NULL, NULL);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001097 }
1098 if (result < 0) {
1099 fprintf(stderr, "Binary log entry conversion failed\n");
1100 }
1101 if (result) {
1102 if (!outRemaining) {
1103 /* make space to leave an indicator */
1104 --outBuf;
1105 ++outRemaining;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001106 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001107 *outBuf++ = (result < 0) ? '!' : '^'; /* Error or Truncation? */
1108 outRemaining--;
1109 /* pretend we ate all the data to prevent log stutter */
1110 inCount = 0;
1111 if (result > 0) result = 0;
1112 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001113
Mark Salyzyn6e315682017-03-09 08:09:43 -08001114 /* eat the silly terminating '\n' */
1115 if (inCount == 1 && *eventData == '\n') {
1116 eventData++;
1117 inCount--;
1118 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001119
Mark Salyzyn6e315682017-03-09 08:09:43 -08001120 if (inCount != 0) {
1121 fprintf(stderr, "Warning: leftover binary log data (%zu bytes)\n", inCount);
1122 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001123
Mark Salyzyn6e315682017-03-09 08:09:43 -08001124 /*
1125 * Terminate the buffer. The NUL byte does not count as part of
1126 * entry->messageLen.
1127 */
1128 *outBuf = '\0';
1129 entry->messageLen = outBuf - messageBuf;
1130 assert(entry->messageLen == (messageBufLen - 1) - outRemaining);
Mark Salyzyn555064b2016-11-11 14:41:30 -08001131
Mark Salyzyn6e315682017-03-09 08:09:43 -08001132 entry->message = messageBuf;
Mark Salyzyn555064b2016-11-11 14:41:30 -08001133
Mark Salyzyn6e315682017-03-09 08:09:43 -08001134 return result;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001135}
1136
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001137/*
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001138 * Convert to printable from message to p buffer, return string length. If p is
1139 * NULL, do not copy, but still return the expected string length.
1140 */
Tom Cherry425317b2019-04-25 13:10:30 -07001141size_t convertPrintable(char* p, const char* message, size_t messageLen) {
Mark Salyzyn6e315682017-03-09 08:09:43 -08001142 char* begin = p;
1143 bool print = p != NULL;
Tom Cherry425317b2019-04-25 13:10:30 -07001144 mbstate_t mb_state = {};
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001145
Mark Salyzyn6e315682017-03-09 08:09:43 -08001146 while (messageLen) {
1147 char buf[6];
1148 ssize_t len = sizeof(buf) - 1;
1149 if ((size_t)len > messageLen) {
1150 len = messageLen;
1151 }
Tom Cherry425317b2019-04-25 13:10:30 -07001152 len = mbrtowc(nullptr, message, len, &mb_state);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001153
1154 if (len < 0) {
Tom Cherry425317b2019-04-25 13:10:30 -07001155 snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
Mark Salyzyn6e315682017-03-09 08:09:43 -08001156 len = 1;
1157 } else {
1158 buf[0] = '\0';
1159 if (len == 1) {
1160 if (*message == '\a') {
1161 strcpy(buf, "\\a");
1162 } else if (*message == '\b') {
1163 strcpy(buf, "\\b");
1164 } else if (*message == '\t') {
1165 strcpy(buf, "\t"); /* Do not escape tabs */
1166 } else if (*message == '\v') {
1167 strcpy(buf, "\\v");
1168 } else if (*message == '\f') {
1169 strcpy(buf, "\\f");
1170 } else if (*message == '\r') {
1171 strcpy(buf, "\\r");
1172 } else if (*message == '\\') {
1173 strcpy(buf, "\\\\");
1174 } else if ((*message < ' ') || (*message & 0x80)) {
Tom Cherry425317b2019-04-25 13:10:30 -07001175 snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001176 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001177 }
1178 if (!buf[0]) {
1179 strncpy(buf, message, len);
1180 buf[len] = '\0';
1181 }
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001182 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001183 if (print) {
1184 strcpy(p, buf);
1185 }
1186 p += strlen(buf);
1187 message += len;
1188 messageLen -= len;
1189 }
1190 return p - begin;
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001191}
1192
Mark Salyzyn6e315682017-03-09 08:09:43 -08001193static char* readSeconds(char* e, struct timespec* t) {
1194 unsigned long multiplier;
1195 char* p;
1196 t->tv_sec = strtoul(e, &p, 10);
1197 if (*p != '.') {
1198 return NULL;
1199 }
1200 t->tv_nsec = 0;
1201 multiplier = NS_PER_SEC;
1202 while (isdigit(*++p) && (multiplier /= 10)) {
1203 t->tv_nsec += (*p - '0') * multiplier;
1204 }
1205 return p;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001206}
1207
Tom Cherryf623f022019-01-10 10:37:36 -08001208static struct timespec* sumTimespec(struct timespec* left, struct timespec* right) {
Mark Salyzyn6e315682017-03-09 08:09:43 -08001209 left->tv_nsec += right->tv_nsec;
1210 left->tv_sec += right->tv_sec;
1211 if (left->tv_nsec >= (long)NS_PER_SEC) {
1212 left->tv_nsec -= NS_PER_SEC;
1213 left->tv_sec += 1;
1214 }
1215 return left;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001216}
1217
Tom Cherryf623f022019-01-10 10:37:36 -08001218static struct timespec* subTimespec(struct timespec* result, struct timespec* left,
Mark Salyzyn6e315682017-03-09 08:09:43 -08001219 struct timespec* right) {
1220 result->tv_nsec = left->tv_nsec - right->tv_nsec;
1221 result->tv_sec = left->tv_sec - right->tv_sec;
1222 if (result->tv_nsec < 0) {
1223 result->tv_nsec += NS_PER_SEC;
1224 result->tv_sec -= 1;
1225 }
1226 return result;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001227}
1228
Mark Salyzyn6e315682017-03-09 08:09:43 -08001229static long long nsecTimespec(struct timespec* now) {
1230 return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001231}
1232
Mark Salyzyn7ad91922016-03-08 16:18:26 -08001233#ifdef __ANDROID__
Tom Cherryf623f022019-01-10 10:37:36 -08001234static void convertMonotonic(struct timespec* result, const AndroidLogEntry* entry) {
Mark Salyzyn6e315682017-03-09 08:09:43 -08001235 struct listnode* node;
1236 struct conversionList {
1237 struct listnode node; /* first */
1238 struct timespec time;
1239 struct timespec convert;
1240 } * list, *next;
1241 struct timespec time, convert;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001242
Mark Salyzyn6e315682017-03-09 08:09:43 -08001243 /* If we do not have a conversion list, build one up */
1244 if (list_empty(&convertHead)) {
1245 bool suspended_pending = false;
Tom Cherryf623f022019-01-10 10:37:36 -08001246 struct timespec suspended_monotonic = {0, 0};
1247 struct timespec suspended_diff = {0, 0};
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001248
Mark Salyzyn6e315682017-03-09 08:09:43 -08001249 /*
1250 * Read dmesg for _some_ synchronization markers and insert
1251 * Anything in the Android Logger before the dmesg logging span will
1252 * be highly suspect regarding the monotonic time calculations.
1253 */
1254 FILE* p = popen("/system/bin/dmesg", "re");
1255 if (p) {
1256 char* line = NULL;
1257 size_t len = 0;
1258 while (getline(&line, &len, p) > 0) {
1259 static const char suspend[] = "PM: suspend entry ";
1260 static const char resume[] = "PM: suspend exit ";
1261 static const char healthd[] = "healthd";
1262 static const char battery[] = ": battery ";
1263 static const char suspended[] = "Suspended for ";
1264 struct timespec monotonic;
1265 struct tm tm;
1266 char *cp, *e = line;
1267 bool add_entry = true;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001268
Mark Salyzyn6e315682017-03-09 08:09:43 -08001269 if (*e == '<') {
1270 while (*e && (*e != '>')) {
1271 ++e;
1272 }
1273 if (*e != '>') {
1274 continue;
1275 }
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001276 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001277 if (*e != '[') {
1278 continue;
1279 }
1280 while (*++e == ' ') {
1281 ;
1282 }
1283 e = readSeconds(e, &monotonic);
1284 if (!e || (*e != ']')) {
1285 continue;
1286 }
1287
1288 if ((e = strstr(e, suspend))) {
1289 e += sizeof(suspend) - 1;
1290 } else if ((e = strstr(line, resume))) {
1291 e += sizeof(resume) - 1;
1292 } else if (((e = strstr(line, healthd))) &&
1293 ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
1294 /* NB: healthd is roughly 150us late, worth the price to
1295 * deal with ntp-induced or hardware clock drift. */
1296 e += sizeof(battery) - 1;
1297 } else if ((e = strstr(line, suspended))) {
1298 e += sizeof(suspended) - 1;
1299 e = readSeconds(e, &time);
1300 if (!e) {
1301 continue;
1302 }
1303 add_entry = false;
1304 suspended_pending = true;
1305 suspended_monotonic = monotonic;
1306 suspended_diff = time;
1307 } else {
1308 continue;
1309 }
1310 if (add_entry) {
1311 /* look for "????-??-?? ??:??:??.????????? UTC" */
1312 cp = strstr(e, " UTC");
1313 if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
1314 continue;
1315 }
1316 e = cp - 29;
1317 cp = readSeconds(cp - 10, &time);
1318 if (!cp) {
1319 continue;
1320 }
1321 cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
1322 if (!cp) {
1323 continue;
1324 }
1325 cp = getenv(tz);
1326 if (cp) {
1327 cp = strdup(cp);
1328 }
1329 setenv(tz, utc, 1);
1330 time.tv_sec = mktime(&tm);
1331 if (cp) {
1332 setenv(tz, cp, 1);
1333 free(cp);
1334 } else {
1335 unsetenv(tz);
1336 }
Tom Cherryf623f022019-01-10 10:37:36 -08001337 list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
Mark Salyzyn6e315682017-03-09 08:09:43 -08001338 list_init(&list->node);
1339 list->time = time;
1340 subTimespec(&list->convert, &time, &monotonic);
1341 list_add_tail(&convertHead, &list->node);
1342 }
1343 if (suspended_pending && !list_empty(&convertHead)) {
Tom Cherryf623f022019-01-10 10:37:36 -08001344 list = node_to_item(list_tail(&convertHead), struct conversionList, node);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001345 if (subTimespec(&time, subTimespec(&time, &list->time, &list->convert),
1346 &suspended_monotonic)
1347 ->tv_sec > 0) {
1348 /* resume, what is convert factor before? */
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001349 subTimespec(&convert, &list->convert, &suspended_diff);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001350 } else {
1351 /* suspend */
1352 convert = list->convert;
1353 }
1354 time = suspended_monotonic;
1355 sumTimespec(&time, &convert);
1356 /* breakpoint just before sleep */
Tom Cherryf623f022019-01-10 10:37:36 -08001357 list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
Mark Salyzyn6e315682017-03-09 08:09:43 -08001358 list_init(&list->node);
1359 list->time = time;
1360 list->convert = convert;
1361 list_add_tail(&convertHead, &list->node);
1362 /* breakpoint just after sleep */
Tom Cherryf623f022019-01-10 10:37:36 -08001363 list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
Mark Salyzyn6e315682017-03-09 08:09:43 -08001364 list_init(&list->node);
1365 list->time = time;
1366 sumTimespec(&list->time, &suspended_diff);
1367 list->convert = convert;
1368 sumTimespec(&list->convert, &suspended_diff);
1369 list_add_tail(&convertHead, &list->node);
1370 suspended_pending = false;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001371 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001372 }
1373 pclose(p);
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001374 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001375 /* last entry is our current time conversion */
Tom Cherryf623f022019-01-10 10:37:36 -08001376 list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
Mark Salyzyn6e315682017-03-09 08:09:43 -08001377 list_init(&list->node);
1378 clock_gettime(CLOCK_REALTIME, &list->time);
1379 clock_gettime(CLOCK_MONOTONIC, &convert);
1380 clock_gettime(CLOCK_MONOTONIC, &time);
1381 /* Correct for instant clock_gettime latency (syscall or ~30ns) */
1382 subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
1383 /* Calculate conversion factor */
1384 subTimespec(&list->convert, &list->time, &time);
1385 list_add_tail(&convertHead, &list->node);
1386 if (suspended_pending) {
1387 /* manufacture a suspend @ point before */
1388 subTimespec(&convert, &list->convert, &suspended_diff);
1389 time = suspended_monotonic;
1390 sumTimespec(&time, &convert);
1391 /* breakpoint just after sleep */
Tom Cherryf623f022019-01-10 10:37:36 -08001392 list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
Mark Salyzyn6e315682017-03-09 08:09:43 -08001393 list_init(&list->node);
1394 list->time = time;
1395 sumTimespec(&list->time, &suspended_diff);
1396 list->convert = convert;
1397 sumTimespec(&list->convert, &suspended_diff);
1398 list_add_head(&convertHead, &list->node);
1399 /* breakpoint just before sleep */
Tom Cherryf623f022019-01-10 10:37:36 -08001400 list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
Mark Salyzyn6e315682017-03-09 08:09:43 -08001401 list_init(&list->node);
1402 list->time = time;
1403 list->convert = convert;
1404 list_add_head(&convertHead, &list->node);
1405 }
1406 }
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001407
Mark Salyzyn6e315682017-03-09 08:09:43 -08001408 /* Find the breakpoint in the conversion list */
1409 list = node_to_item(list_head(&convertHead), struct conversionList, node);
1410 next = NULL;
1411 list_for_each(node, &convertHead) {
1412 next = node_to_item(node, struct conversionList, node);
1413 if (entry->tv_sec < next->time.tv_sec) {
1414 break;
1415 } else if (entry->tv_sec == next->time.tv_sec) {
1416 if (entry->tv_nsec < next->time.tv_nsec) {
1417 break;
1418 }
1419 }
1420 list = next;
1421 }
1422
1423 /* blend time from one breakpoint to the next */
1424 convert = list->convert;
1425 if (next) {
1426 unsigned long long total, run;
1427
1428 total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
1429 time.tv_sec = entry->tv_sec;
1430 time.tv_nsec = entry->tv_nsec;
1431 run = nsecTimespec(subTimespec(&time, &time, &list->time));
1432 if (run < total) {
1433 long long crun;
1434
1435 float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
1436 f *= run;
1437 f /= total;
1438 crun = f;
1439 convert.tv_sec += crun / (long long)NS_PER_SEC;
1440 if (crun < 0) {
1441 convert.tv_nsec -= (-crun) % NS_PER_SEC;
1442 if (convert.tv_nsec < 0) {
1443 convert.tv_nsec += NS_PER_SEC;
1444 convert.tv_sec -= 1;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001445 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001446 } else {
1447 convert.tv_nsec += crun % NS_PER_SEC;
1448 if (convert.tv_nsec >= (long)NS_PER_SEC) {
1449 convert.tv_nsec -= NS_PER_SEC;
1450 convert.tv_sec += 1;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001451 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001452 }
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001453 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001454 }
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001455
Mark Salyzyn6e315682017-03-09 08:09:43 -08001456 /* Apply the correction factor */
1457 result->tv_sec = entry->tv_sec;
1458 result->tv_nsec = entry->tv_nsec;
1459 subTimespec(result, result, &convert);
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001460}
Mark Salyzyn7ad91922016-03-08 16:18:26 -08001461#endif
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001462
The Android Open Source Project32315d42008-10-21 07:00:00 -07001463/**
1464 * Formats a log message into a buffer
1465 *
1466 * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
1467 * If return value != defaultBuffer, caller must call free()
1468 * Returns NULL on malloc error
1469 */
1470
Tom Cherry3d6a8782019-02-08 11:46:19 -08001471char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
1472 size_t defaultBufferSize, const AndroidLogEntry* entry,
1473 size_t* p_outLength) {
Yabin Cui945b83f2014-11-13 10:02:08 -08001474#if !defined(_WIN32)
Mark Salyzyn6e315682017-03-09 08:09:43 -08001475 struct tm tmBuf;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001476#endif
Mark Salyzyn6e315682017-03-09 08:09:43 -08001477 struct tm* ptm;
1478 /* good margin, 23+nul for msec, 26+nul for usec, 29+nul to nsec */
1479 char timeBuf[64];
1480 char prefixBuf[128], suffixBuf[128];
1481 char priChar;
1482 int prefixSuffixIsHeaderFooter = 0;
1483 char* ret;
1484 time_t now;
1485 unsigned long nsec;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001486
Mark Salyzyn6e315682017-03-09 08:09:43 -08001487 priChar = filterPriToChar(entry->priority);
1488 size_t prefixLen = 0, suffixLen = 0;
1489 size_t len;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001490
Mark Salyzyn6e315682017-03-09 08:09:43 -08001491 /*
1492 * Get the current date/time in pretty form
1493 *
1494 * It's often useful when examining a log with "less" to jump to
1495 * a specific point in the file by searching for the date/time stamp.
1496 * For this reason it's very annoying to have regexp meta characters
1497 * in the time stamp. Don't use forward slashes, parenthesis,
1498 * brackets, asterisks, or other special chars here.
1499 *
1500 * The caller may have affected the timezone environment, this is
1501 * expected to be sensitive to that.
1502 */
1503 now = entry->tv_sec;
1504 nsec = entry->tv_nsec;
Mark Salyzyn7ad91922016-03-08 16:18:26 -08001505#if __ANDROID__
Mark Salyzyn6e315682017-03-09 08:09:43 -08001506 if (p_format->monotonic_output) {
1507 /* prevent convertMonotonic from being called if logd is monotonic */
1508 if (android_log_clockid() != CLOCK_MONOTONIC) {
1509 struct timespec time;
1510 convertMonotonic(&time, entry);
1511 now = time.tv_sec;
1512 nsec = time.tv_nsec;
Mark Salyzyn4d0473f2015-08-31 15:53:41 -07001513 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001514 }
Mark Salyzyn7ad91922016-03-08 16:18:26 -08001515#endif
Mark Salyzyn6e315682017-03-09 08:09:43 -08001516 if (now < 0) {
1517 nsec = NS_PER_SEC - nsec;
1518 }
1519 if (p_format->epoch_output || p_format->monotonic_output) {
1520 ptm = NULL;
Tom Cherryf623f022019-01-10 10:37:36 -08001521 snprintf(timeBuf, sizeof(timeBuf), p_format->monotonic_output ? "%6lld" : "%19lld",
1522 (long long)now);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001523 } else {
Yabin Cui945b83f2014-11-13 10:02:08 -08001524#if !defined(_WIN32)
Mark Salyzyn6e315682017-03-09 08:09:43 -08001525 ptm = localtime_r(&now, &tmBuf);
The Android Open Source Project32315d42008-10-21 07:00:00 -07001526#else
Mark Salyzyn6e315682017-03-09 08:09:43 -08001527 ptm = localtime(&now);
The Android Open Source Project32315d42008-10-21 07:00:00 -07001528#endif
Tom Cherryf623f022019-01-10 10:37:36 -08001529 strftime(timeBuf, sizeof(timeBuf), &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001530 }
1531 len = strlen(timeBuf);
1532 if (p_format->nsec_time_output) {
1533 len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%09ld", nsec);
1534 } else if (p_format->usec_time_output) {
Tom Cherryf623f022019-01-10 10:37:36 -08001535 len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld", nsec / US_PER_NSEC);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001536 } else {
Tom Cherryf623f022019-01-10 10:37:36 -08001537 len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld", nsec / MS_PER_NSEC);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001538 }
1539 if (p_format->zone_output && ptm) {
1540 strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
1541 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001542
Mark Salyzyn6e315682017-03-09 08:09:43 -08001543 /*
1544 * Construct a buffer containing the log header and log message.
1545 */
1546 if (p_format->colored_output) {
Tom Cherryf623f022019-01-10 10:37:36 -08001547 prefixLen =
1548 snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", colorFromPri(entry->priority));
Mark Salyzyn6e315682017-03-09 08:09:43 -08001549 prefixLen = MIN(prefixLen, sizeof(prefixBuf));
George Burgess IV3d8ca6b2018-05-15 18:56:36 -07001550
1551 const char suffixContents[] = "\x1B[0m";
1552 strcpy(suffixBuf, suffixContents);
1553 suffixLen = strlen(suffixContents);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001554 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001555
Mark Salyzyn6e315682017-03-09 08:09:43 -08001556 char uid[16];
1557 uid[0] = '\0';
1558 if (p_format->uid_output) {
1559 if (entry->uid >= 0) {
1560/*
1561 * This code is Android specific, bionic guarantees that
1562 * calls to non-reentrant getpwuid() are thread safe.
1563 */
Mark Salyzyn7ad91922016-03-08 16:18:26 -08001564#if !defined(__MINGW32__)
1565#if (FAKE_LOG_DEVICE == 0)
William Roberts6877c6c2016-04-08 12:13:17 -07001566#ifndef __BIONIC__
Tom Cherryf623f022019-01-10 10:37:36 -08001567#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
William Roberts6877c6c2016-04-08 12:13:17 -07001568#endif
Mark Salyzyn7ad91922016-03-08 16:18:26 -08001569#endif
Mark Salyzyn6e315682017-03-09 08:09:43 -08001570 struct passwd* pwd = getpwuid(entry->uid);
1571 if (pwd && (strlen(pwd->pw_name) <= 5)) {
1572 snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
1573 } else
Mark Salyzyn7ad91922016-03-08 16:18:26 -08001574#endif
Mark Salyzyn6e315682017-03-09 08:09:43 -08001575 {
1576 /* Not worth parsing package list, names all longer than 5 */
1577 snprintf(uid, sizeof(uid), "%5d:", entry->uid);
1578 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001579 } else {
Mark Salyzyn6e315682017-03-09 08:09:43 -08001580 snprintf(uid, sizeof(uid), " ");
The Android Open Source Project32315d42008-10-21 07:00:00 -07001581 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001582 }
1583
1584 switch (p_format->format) {
1585 case FORMAT_TAG:
Tom Cherryf623f022019-01-10 10:37:36 -08001586 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8.*s: ", priChar,
1587 (int)entry->tagLen, entry->tag);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001588 strcpy(suffixBuf + suffixLen, "\n");
1589 ++suffixLen;
1590 break;
1591 case FORMAT_PROCESS:
Tom Cherryf623f022019-01-10 10:37:36 -08001592 len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen, " (%.*s)\n",
1593 (int)entry->tagLen, entry->tag);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001594 suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
Tom Cherryf623f022019-01-10 10:37:36 -08001595 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d) ", priChar,
1596 uid, entry->pid);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001597 break;
1598 case FORMAT_THREAD:
Tom Cherryf623f022019-01-10 10:37:36 -08001599 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d:%5d) ",
1600 priChar, uid, entry->pid, entry->tid);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001601 strcpy(suffixBuf + suffixLen, "\n");
1602 ++suffixLen;
1603 break;
1604 case FORMAT_RAW:
1605 prefixBuf[prefixLen] = 0;
1606 len = 0;
1607 strcpy(suffixBuf + suffixLen, "\n");
1608 ++suffixLen;
1609 break;
1610 case FORMAT_TIME:
1611 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Tom Cherryf623f022019-01-10 10:37:36 -08001612 "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar, (int)entry->tagLen, entry->tag, uid,
1613 entry->pid);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001614 strcpy(suffixBuf + suffixLen, "\n");
1615 ++suffixLen;
1616 break;
1617 case FORMAT_THREADTIME:
1618 ret = strchr(uid, ':');
1619 if (ret) {
1620 *ret = ' ';
1621 }
1622 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Tom Cherryf623f022019-01-10 10:37:36 -08001623 "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid, entry->tid, priChar,
1624 (int)entry->tagLen, entry->tag);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001625 strcpy(suffixBuf + suffixLen, "\n");
1626 ++suffixLen;
1627 break;
1628 case FORMAT_LONG:
1629 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Tom Cherryf623f022019-01-10 10:37:36 -08001630 "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid, entry->tid, priChar,
1631 (int)entry->tagLen, entry->tag);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001632 strcpy(suffixBuf + suffixLen, "\n\n");
1633 suffixLen += 2;
1634 prefixSuffixIsHeaderFooter = 1;
1635 break;
1636 case FORMAT_BRIEF:
1637 default:
Tom Cherryf623f022019-01-10 10:37:36 -08001638 len =
1639 snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
1640 "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen, entry->tag, uid, entry->pid);
Mark Salyzyn6e315682017-03-09 08:09:43 -08001641 strcpy(suffixBuf + suffixLen, "\n");
1642 ++suffixLen;
1643 break;
1644 }
1645
1646 /* snprintf has a weird return value. It returns what would have been
1647 * written given a large enough buffer. In the case that the prefix is
1648 * longer then our buffer(128), it messes up the calculations below
1649 * possibly causing heap corruption. To avoid this we double check and
1650 * set the length at the maximum (size minus null byte)
1651 */
1652 prefixLen += len;
1653 if (prefixLen >= sizeof(prefixBuf)) {
1654 prefixLen = sizeof(prefixBuf) - 1;
1655 prefixBuf[sizeof(prefixBuf) - 1] = '\0';
1656 }
1657 if (suffixLen >= sizeof(suffixBuf)) {
1658 suffixLen = sizeof(suffixBuf) - 1;
1659 suffixBuf[sizeof(suffixBuf) - 2] = '\n';
1660 suffixBuf[sizeof(suffixBuf) - 1] = '\0';
1661 }
1662
1663 /* the following code is tragically unreadable */
1664
1665 size_t numLines;
1666 char* p;
1667 size_t bufferSize;
1668 const char* pm;
1669
1670 if (prefixSuffixIsHeaderFooter) {
1671 /* we're just wrapping message with a header/footer */
1672 numLines = 1;
1673 } else {
1674 pm = entry->message;
1675 numLines = 0;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001676
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001677 /*
Mark Salyzyn6e315682017-03-09 08:09:43 -08001678 * The line-end finding here must match the line-end finding
1679 * in for ( ... numLines...) loop below
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001680 */
Mark Salyzyn6e315682017-03-09 08:09:43 -08001681 while (pm < (entry->message + entry->messageLen)) {
1682 if (*pm++ == '\n') numLines++;
1683 }
1684 /* plus one line for anything not newline-terminated at the end */
1685 if (pm > entry->message && *(pm - 1) != '\n') numLines++;
1686 }
1687
1688 /*
1689 * this is an upper bound--newlines in message may be counted
1690 * extraneously
1691 */
1692 bufferSize = (numLines * (prefixLen + suffixLen)) + 1;
1693 if (p_format->printable_output) {
1694 /* Calculate extra length to convert non-printable to printable */
1695 bufferSize += convertPrintable(NULL, entry->message, entry->messageLen);
1696 } else {
1697 bufferSize += entry->messageLen;
1698 }
1699
1700 if (defaultBufferSize >= bufferSize) {
1701 ret = defaultBuffer;
1702 } else {
1703 ret = (char*)malloc(bufferSize);
1704
1705 if (ret == NULL) {
1706 return ret;
1707 }
1708 }
1709
1710 ret[0] = '\0'; /* to start strcat off */
1711
1712 p = ret;
1713 pm = entry->message;
1714
1715 if (prefixSuffixIsHeaderFooter) {
1716 strcat(p, prefixBuf);
1717 p += prefixLen;
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001718 if (p_format->printable_output) {
Mark Salyzyn6e315682017-03-09 08:09:43 -08001719 p += convertPrintable(p, entry->message, entry->messageLen);
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001720 } else {
Mark Salyzyn6e315682017-03-09 08:09:43 -08001721 strncat(p, entry->message, entry->messageLen);
1722 p += entry->messageLen;
Mark Salyzyn1ebdb352015-05-15 09:01:58 -07001723 }
Mark Salyzyn6e315682017-03-09 08:09:43 -08001724 strcat(p, suffixBuf);
1725 p += suffixLen;
1726 } else {
1727 do {
1728 const char* lineStart;
1729 size_t lineLen;
1730 lineStart = pm;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001731
Mark Salyzyn6e315682017-03-09 08:09:43 -08001732 /* Find the next end-of-line in message */
1733 while (pm < (entry->message + entry->messageLen) && *pm != '\n') pm++;
1734 lineLen = pm - lineStart;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001735
Mark Salyzyn6e315682017-03-09 08:09:43 -08001736 strcat(p, prefixBuf);
1737 p += prefixLen;
1738 if (p_format->printable_output) {
1739 p += convertPrintable(p, lineStart, lineLen);
1740 } else {
1741 strncat(p, lineStart, lineLen);
1742 p += lineLen;
1743 }
1744 strcat(p, suffixBuf);
1745 p += suffixLen;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001746
Mark Salyzyn6e315682017-03-09 08:09:43 -08001747 if (*pm == '\n') pm++;
1748 } while (pm < (entry->message + entry->messageLen));
1749 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001750
Mark Salyzyn6e315682017-03-09 08:09:43 -08001751 if (p_outLength != NULL) {
1752 *p_outLength = p - ret;
1753 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001754
Mark Salyzyn6e315682017-03-09 08:09:43 -08001755 return ret;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001756}
1757
1758/**
1759 * Either print or do not print log line, based on filter
1760 *
1761 * Returns count bytes written
1762 */
1763
Tom Cherry3d6a8782019-02-08 11:46:19 -08001764int android_log_printLogLine(AndroidLogFormat* p_format, int fd, const AndroidLogEntry* entry) {
Mark Salyzyn6e315682017-03-09 08:09:43 -08001765 int ret;
1766 char defaultBuffer[512];
1767 char* outBuffer = NULL;
1768 size_t totalLen;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001769
Tom Cherryf623f022019-01-10 10:37:36 -08001770 outBuffer =
1771 android_log_formatLogLine(p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
The Android Open Source Project32315d42008-10-21 07:00:00 -07001772
Mark Salyzyn6e315682017-03-09 08:09:43 -08001773 if (!outBuffer) return -1;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001774
Mark Salyzyn6e315682017-03-09 08:09:43 -08001775 do {
1776 ret = write(fd, outBuffer, totalLen);
1777 } while (ret < 0 && errno == EINTR);
The Android Open Source Project32315d42008-10-21 07:00:00 -07001778
Mark Salyzyn6e315682017-03-09 08:09:43 -08001779 if (ret < 0) {
1780 fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
1781 ret = 0;
1782 goto done;
1783 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001784
Mark Salyzyn6e315682017-03-09 08:09:43 -08001785 if (((size_t)ret) < totalLen) {
1786 fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, (int)totalLen);
1787 goto done;
1788 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001789
1790done:
Mark Salyzyn6e315682017-03-09 08:09:43 -08001791 if (outBuffer != defaultBuffer) {
1792 free(outBuffer);
1793 }
The Android Open Source Project32315d42008-10-21 07:00:00 -07001794
Mark Salyzyn6e315682017-03-09 08:09:43 -08001795 return ret;
The Android Open Source Project32315d42008-10-21 07:00:00 -07001796}