• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 #pragma once
18 
19 #include <inttypes.h>
20 #include <time.h>
21 
22 #include <android-base/logging.h>
23 #include <android-base/stringprintf.h>
24 
25 #define LOGGER_ENTRY_MAX_PAYLOAD 4068  // This constant is not in the NDK.
26 
27 namespace android {
28 namespace base {
29 
30 // This splits the message up line by line, by calling log_function with a pointer to the start of
31 // each line and the size up to the newline character.  It sends size = -1 for the final line.
32 template <typename F, typename... Args>
SplitByLines(const char * msg,const F & log_function,Args &&...args)33 static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
34   const char* newline = strchr(msg, '\n');
35   while (newline != nullptr) {
36     log_function(msg, newline - msg, args...);
37     msg = newline + 1;
38     newline = strchr(msg, '\n');
39   }
40 
41   log_function(msg, -1, args...);
42 }
43 
44 // This splits the message up into chunks that logs can process delimited by new lines.  It calls
45 // log_function with the exact null terminated message that should be sent to logd.
46 // Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
47 // this function simply calls log_function with msg without any extra overhead.
48 template <typename F>
SplitByLogdChunks(LogId log_id,LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * msg,const F & log_function)49 static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
50                               unsigned int line, const char* msg, const F& log_function) {
51   // The maximum size of a payload, after the log header that logd will accept is
52   // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
53   // the string that we can log in each pass.
54   // The protocol is documented in liblog/README.protocol.md.
55   // Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
56   // and an additional byte for the null terminator on the payload.  We subtract an additional 32
57   // bytes for slack, similar to java/android/util/Log.java.
58   ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
59   if (max_size <= 0) {
60     abort();
61   }
62   // If we're logging a fatal message, we'll append the file and line numbers.
63   bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
64 
65   std::string file_header;
66   if (add_file) {
67     file_header = StringPrintf("%s:%u] ", file, line);
68   }
69   int file_header_size = file_header.size();
70 
71   __attribute__((uninitialized)) char logd_chunk[max_size + 1];
72   ptrdiff_t chunk_position = 0;
73 
74   auto call_log_function = [&]() {
75     log_function(log_id, severity, tag, logd_chunk);
76     chunk_position = 0;
77   };
78 
79   auto write_to_logd_chunk = [&](const char* message, int length) {
80     int size_written = 0;
81     const char* new_line = chunk_position > 0 ? "\n" : "";
82     if (add_file) {
83       size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
84                               "%s%s%.*s", new_line, file_header.c_str(), length, message);
85     } else {
86       size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
87                               "%s%.*s", new_line, length, message);
88     }
89 
90     // This should never fail, if it does and we set size_written to 0, which will skip this line
91     // and move to the next one.
92     if (size_written < 0) {
93       size_written = 0;
94     }
95     chunk_position += size_written;
96   };
97 
98   const char* newline = strchr(msg, '\n');
99   while (newline != nullptr) {
100     // If we have data in the buffer and this next line doesn't fit, write the buffer.
101     if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
102       call_log_function();
103     }
104 
105     // Otherwise, either the next line fits or we have any empty buffer and too large of a line to
106     // ever fit, in both cases, we add it to the buffer and continue.
107     write_to_logd_chunk(msg, newline - msg);
108 
109     msg = newline + 1;
110     newline = strchr(msg, '\n');
111   }
112 
113   // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
114   // then write the buffer.
115   if (chunk_position != 0 &&
116       chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) {
117     write_to_logd_chunk(msg, -1);
118     call_log_function();
119   } else {
120     // If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
121     if (chunk_position != 0) {
122       call_log_function();
123     }
124     // Then write the rest of the msg.
125     if (add_file) {
126       snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
127       log_function(log_id, severity, tag, logd_chunk);
128     } else {
129       log_function(log_id, severity, tag, msg);
130     }
131   }
132 }
133 
CountSizeAndNewLines(const char * message)134 static std::pair<int, int> CountSizeAndNewLines(const char* message) {
135   int size = 0;
136   int new_lines = 0;
137   while (*message != '\0') {
138     size++;
139     if (*message == '\n') {
140       ++new_lines;
141     }
142     ++message;
143   }
144   return {size, new_lines};
145 }
146 
147 // This adds the log header to each line of message and returns it as a string intended to be
148 // written to stderr.
StderrOutputGenerator(const struct timespec & ts,int pid,uint64_t tid,LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)149 static std::string StderrOutputGenerator(const struct timespec& ts, int pid, uint64_t tid,
150                                          LogSeverity severity, const char* tag, const char* file,
151                                          unsigned int line, const char* message) {
152   struct tm now;
153   localtime_r(&ts.tv_sec, &now);
154   char timestamp[sizeof("mm-DD HH:MM:SS.mmm\0")];
155   size_t n = strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
156   snprintf(timestamp + n, sizeof(timestamp) - n, ".%03ld", ts.tv_nsec / (1000 * 1000));
157 
158   static const char log_characters[] = "VDIWEFF";
159   static_assert(arraysize(log_characters) - 1 == FATAL + 1,
160                 "Mismatch in size of log_characters and values in LogSeverity");
161   char severity_char = log_characters[severity];
162   std::string line_prefix;
163   const char* real_tag = tag ? tag : "nullptr";
164   if (file != nullptr) {
165     line_prefix = StringPrintf("%s %5d %5" PRIu64 " %c %-8s: %s:%u ", timestamp, pid, tid,
166                                severity_char, real_tag, file, line);
167   } else {
168     line_prefix =
169         StringPrintf("%s %5d %5" PRIu64 " %c %-8s: ", timestamp, pid, tid, severity_char, real_tag);
170   }
171 
172   auto [size, new_lines] = CountSizeAndNewLines(message);
173   std::string output_string;
174   output_string.reserve(size + new_lines * line_prefix.size() + 1);
175 
176   auto concat_lines = [&](const char* message, int size) {
177     output_string.append(line_prefix);
178     if (size == -1) {
179       output_string.append(message);
180     } else {
181       output_string.append(message, size);
182     }
183     output_string.append("\n");
184   };
185   SplitByLines(message, concat_lines);
186   return output_string;
187 }
188 
189 }  // namespace base
190 }  // namespace android
191