1 /*
2 * Copyright (C) 2012-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "LogBufferElement.h"
18
19 #include <ctype.h>
20 #include <endian.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <time.h>
25 #include <unistd.h>
26
27 #include <log/log_read.h>
28 #include <private/android_logger.h>
29
30 #include "LogStatistics.h"
31 #include "LogUtils.h"
32
LogBufferElement(log_id_t log_id,log_time realtime,uid_t uid,pid_t pid,pid_t tid,uint64_t sequence,const char * msg,uint16_t len)33 LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
34 pid_t tid, uint64_t sequence, const char* msg, uint16_t len)
35 : uid_(uid),
36 pid_(pid),
37 tid_(tid),
38 sequence_(sequence),
39 realtime_(realtime),
40 msg_len_(len),
41 log_id_(log_id),
42 dropped_(false) {
43 msg_ = new char[len];
44 memcpy(msg_, msg, len);
45 }
46
LogBufferElement(const LogBufferElement & elem)47 LogBufferElement::LogBufferElement(const LogBufferElement& elem)
48 : uid_(elem.uid_),
49 pid_(elem.pid_),
50 tid_(elem.tid_),
51 sequence_(elem.sequence_),
52 realtime_(elem.realtime_),
53 msg_len_(elem.msg_len_),
54 log_id_(elem.log_id_),
55 dropped_(elem.dropped_) {
56 if (dropped_) {
57 tag_ = elem.GetTag();
58 } else {
59 msg_ = new char[msg_len_];
60 memcpy(msg_, elem.msg_, msg_len_);
61 }
62 }
63
LogBufferElement(LogBufferElement && elem)64 LogBufferElement::LogBufferElement(LogBufferElement&& elem) noexcept
65 : uid_(elem.uid_),
66 pid_(elem.pid_),
67 tid_(elem.tid_),
68 sequence_(elem.sequence_),
69 realtime_(elem.realtime_),
70 msg_len_(elem.msg_len_),
71 log_id_(elem.log_id_),
72 dropped_(elem.dropped_) {
73 if (dropped_) {
74 tag_ = elem.GetTag();
75 } else {
76 msg_ = elem.msg_;
77 elem.msg_ = nullptr;
78 }
79 }
80
~LogBufferElement()81 LogBufferElement::~LogBufferElement() {
82 if (!dropped_) {
83 delete[] msg_;
84 }
85 }
86
GetTag() const87 uint32_t LogBufferElement::GetTag() const {
88 // Binary buffers have no tag.
89 if (!IsBinary(log_id())) {
90 return 0;
91 }
92
93 // Dropped messages store the tag in place of msg_.
94 if (dropped_) {
95 return tag_;
96 }
97
98 return MsgToTag(msg(), msg_len());
99 }
100
ToLogStatisticsElement() const101 LogStatisticsElement LogBufferElement::ToLogStatisticsElement() const {
102 // Estimate the size of this element in the parent std::list<> by adding two void*'s
103 // corresponding to the next/prev pointers and aligning to 64 bit.
104 uint16_t element_in_list_size =
105 (sizeof(*this) + 2 * sizeof(void*) + sizeof(uint64_t) - 1) & -sizeof(uint64_t);
106 return LogStatisticsElement{
107 .uid = uid(),
108 .pid = pid(),
109 .tid = tid(),
110 .tag = GetTag(),
111 .realtime = realtime(),
112 .msg = msg(),
113 .msg_len = msg_len(),
114 .dropped_count = dropped_count(),
115 .log_id = log_id(),
116 .total_len = static_cast<uint16_t>(element_in_list_size + msg_len()),
117 };
118 }
119
SetDropped(uint16_t value)120 uint16_t LogBufferElement::SetDropped(uint16_t value) {
121 if (dropped_) {
122 return dropped_count_ = value;
123 }
124
125 // The tag information is saved in msg_ data, which is in a union with tag_, used after dropped_
126 // is set to true. Therefore we save the tag value aside, delete msg_, then set tag_ to the tag
127 // value in its place.
128 auto old_tag = GetTag();
129 delete[] msg_;
130 msg_ = nullptr;
131
132 tag_ = old_tag;
133 dropped_ = true;
134 return dropped_count_ = value;
135 }
136
137 // caller must own and free character string
tidToName(pid_t tid)138 char* android::tidToName(pid_t tid) {
139 char* retval = nullptr;
140 char buffer[256];
141 snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
142 int fd = open(buffer, O_RDONLY | O_CLOEXEC);
143 if (fd >= 0) {
144 ssize_t ret = read(fd, buffer, sizeof(buffer));
145 if (ret >= (ssize_t)sizeof(buffer)) {
146 ret = sizeof(buffer) - 1;
147 }
148 while ((ret > 0) && isspace(buffer[ret - 1])) {
149 --ret;
150 }
151 if (ret > 0) {
152 buffer[ret] = '\0';
153 retval = strdup(buffer);
154 }
155 close(fd);
156 }
157
158 // if nothing for comm, check out cmdline
159 char* name = android::pidToName(tid);
160 if (!retval) {
161 retval = name;
162 name = nullptr;
163 }
164
165 // check if comm is truncated, see if cmdline has full representation
166 if (name) {
167 // impossible for retval to be NULL if name not NULL
168 size_t retval_len = strlen(retval);
169 size_t name_len = strlen(name);
170 // KISS: ToDo: Only checks prefix truncated, not suffix, or both
171 if ((retval_len < name_len) &&
172 !fastcmp<strcmp>(retval, name + name_len - retval_len)) {
173 free(retval);
174 retval = name;
175 } else {
176 free(name);
177 }
178 }
179 return retval;
180 }
181
182 // assumption: msg_ == NULL
PopulateDroppedMessage(char * & buffer,LogStatistics * stats,bool lastSame)183 size_t LogBufferElement::PopulateDroppedMessage(char*& buffer, LogStatistics* stats,
184 bool lastSame) {
185 static const char tag[] = "chatty";
186
187 if (!__android_log_is_loggable_len(ANDROID_LOG_INFO, tag, strlen(tag),
188 ANDROID_LOG_VERBOSE)) {
189 return 0;
190 }
191
192 static const char format_uid[] = "uid=%u%s%s %s %u line%s";
193 const char* name = stats->UidToName(uid_);
194 const char* commName = android::tidToName(tid_);
195 if (!commName && (tid_ != pid_)) {
196 commName = android::tidToName(pid_);
197 }
198 if (!commName) {
199 commName = stats->PidToName(pid_);
200 }
201 if (name && name[0] && commName && (name[0] == commName[0])) {
202 size_t len = strlen(name + 1);
203 if (!strncmp(name + 1, commName + 1, len)) {
204 if (commName[len + 1] == '\0') {
205 free(const_cast<char*>(commName));
206 commName = nullptr;
207 } else {
208 free(const_cast<char*>(name));
209 name = nullptr;
210 }
211 }
212 }
213 if (name) {
214 char* buf = nullptr;
215 int result = asprintf(&buf, "(%s)", name);
216 if (result != -1) {
217 free(const_cast<char*>(name));
218 name = buf;
219 }
220 }
221 if (commName) {
222 char* buf = nullptr;
223 int result = asprintf(&buf, " %s", commName);
224 if (result != -1) {
225 free(const_cast<char*>(commName));
226 commName = buf;
227 }
228 }
229 // identical to below to calculate the buffer size required
230 const char* type = lastSame ? "identical" : "expire";
231 size_t len = snprintf(nullptr, 0, format_uid, uid_, name ? name : "", commName ? commName : "",
232 type, dropped_count(), (dropped_count() > 1) ? "s" : "");
233
234 size_t hdrLen;
235 if (IsBinary(log_id())) {
236 hdrLen = sizeof(android_log_event_string_t);
237 } else {
238 hdrLen = 1 + sizeof(tag);
239 }
240
241 buffer = static_cast<char*>(calloc(1, hdrLen + len + 1));
242 if (!buffer) {
243 free(const_cast<char*>(name));
244 free(const_cast<char*>(commName));
245 return 0;
246 }
247
248 size_t retval = hdrLen + len;
249 if (IsBinary(log_id())) {
250 android_log_event_string_t* event =
251 reinterpret_cast<android_log_event_string_t*>(buffer);
252
253 event->header.tag = htole32(CHATTY_LOG_TAG);
254 event->type = EVENT_TYPE_STRING;
255 event->length = htole32(len);
256 } else {
257 ++retval;
258 buffer[0] = ANDROID_LOG_INFO;
259 strcpy(buffer + 1, tag);
260 }
261
262 snprintf(buffer + hdrLen, len + 1, format_uid, uid_, name ? name : "", commName ? commName : "",
263 type, dropped_count(), (dropped_count() > 1) ? "s" : "");
264 free(const_cast<char*>(name));
265 free(const_cast<char*>(commName));
266
267 return retval;
268 }
269
FlushTo(LogWriter * writer,LogStatistics * stats,bool lastSame)270 bool LogBufferElement::FlushTo(LogWriter* writer, LogStatistics* stats, bool lastSame) {
271 struct logger_entry entry = {};
272
273 entry.hdr_size = sizeof(struct logger_entry);
274 entry.lid = log_id_;
275 entry.pid = pid_;
276 entry.tid = tid_;
277 entry.uid = uid_;
278 entry.sec = realtime_.tv_sec;
279 entry.nsec = realtime_.tv_nsec;
280
281 char* buffer = nullptr;
282 const char* msg;
283 if (dropped_) {
284 entry.len = PopulateDroppedMessage(buffer, stats, lastSame);
285 if (!entry.len) return true;
286 msg = buffer;
287 } else {
288 msg = msg_;
289 entry.len = msg_len_;
290 }
291
292 bool retval = writer->Write(entry, msg);
293
294 if (buffer) free(buffer);
295
296 return retval;
297 }
298