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