Coverage Report

Created: 2026-09-14 06:17

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