blob: 4511e0b8ee21e9c6707b06ea27407ae8674d7832 [file] [log] [blame]
Mark Salyzynd774bce2014-02-06 14:48:50 -08001/*
2 * Copyright (C) 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
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070017#include <algorithm> // std::max
Mark Salyzyn5766f802014-04-07 07:05:40 -070018#include <fcntl.h>
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070019#include <stdio.h>
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070020#include <string.h>
21#include <unistd.h>
Mark Salyzynd774bce2014-02-06 14:48:50 -080022
23#include <log/logger.h>
24#include <private/android_filesystem_config.h>
25#include <utils/String8.h>
26
27#include "LogStatistics.h"
28
Mark Salyzyn1435b472015-03-16 08:26:05 -070029LogStatistics::LogStatistics()
30 : enable(false) {
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070031 log_id_for_each(id) {
32 mSizes[id] = 0;
33 mElements[id] = 0;
34 mSizesTotal[id] = 0;
35 mElementsTotal[id] = 0;
Mark Salyzynd774bce2014-02-06 14:48:50 -080036 }
37}
38
Mark Salyzyn1435b472015-03-16 08:26:05 -070039namespace android {
40
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070041// caller must own and free character string
Mark Salyzyn14d39b32015-04-13 14:24:45 -070042char *pidToName(pid_t pid) {
Mark Salyzyn5766f802014-04-07 07:05:40 -070043 char *retval = NULL;
Mark Salyzyn0f827bc2014-09-21 14:22:18 -070044 if (pid == 0) { // special case from auditd for kernel
45 retval = strdup("logd.auditd");
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070046 } else {
Mark Salyzyn5766f802014-04-07 07:05:40 -070047 char buffer[512];
48 snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
49 int fd = open(buffer, O_RDONLY);
50 if (fd >= 0) {
51 ssize_t ret = read(fd, buffer, sizeof(buffer));
52 if (ret > 0) {
53 buffer[sizeof(buffer)-1] = '\0';
54 // frameworks intermediate state
55 if (strcmp(buffer, "<pre-initialized>")) {
56 retval = strdup(buffer);
57 }
58 }
59 close(fd);
60 }
61 }
62 return retval;
63}
64
Mark Salyzyn1435b472015-03-16 08:26:05 -070065}
66
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070067void LogStatistics::add(LogBufferElement *e) {
68 log_id_t log_id = e->getLogId();
69 unsigned short size = e->getMsgLen();
Mark Salyzynd774bce2014-02-06 14:48:50 -080070 mSizes[log_id] += size;
71 ++mElements[log_id];
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070072
Mark Salyzyn14d39b32015-04-13 14:24:45 -070073 uidTable[log_id].add(e->getUid(), e);
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070074
75 mSizesTotal[log_id] += size;
76 ++mElementsTotal[log_id];
Mark Salyzyn1435b472015-03-16 08:26:05 -070077
78 if (!enable) {
79 return;
80 }
81
Mark Salyzyn14d39b32015-04-13 14:24:45 -070082 pidTable.add(e->getPid(), e);
Mark Salyzynf96ac902015-04-13 14:24:45 -070083
84 uint32_t tag = e->getTag();
85 if (tag) {
86 tagTable.add(tag, e);
87 }
Mark Salyzynd774bce2014-02-06 14:48:50 -080088}
89
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070090void LogStatistics::subtract(LogBufferElement *e) {
91 log_id_t log_id = e->getLogId();
92 unsigned short size = e->getMsgLen();
Mark Salyzynd774bce2014-02-06 14:48:50 -080093 mSizes[log_id] -= size;
94 --mElements[log_id];
Mark Salyzyn9cf5d402015-03-10 13:51:35 -070095
Mark Salyzyn14d39b32015-04-13 14:24:45 -070096 uidTable[log_id].subtract(e->getUid(), e);
Mark Salyzynd774bce2014-02-06 14:48:50 -080097
Mark Salyzyn1435b472015-03-16 08:26:05 -070098 if (!enable) {
99 return;
Mark Salyzynd774bce2014-02-06 14:48:50 -0800100 }
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700101
Mark Salyzyn14d39b32015-04-13 14:24:45 -0700102 pidTable.subtract(e->getPid(), e);
Mark Salyzynf96ac902015-04-13 14:24:45 -0700103
104 uint32_t tag = e->getTag();
105 if (tag) {
106 tagTable.subtract(tag, e);
107 }
Mark Salyzynd774bce2014-02-06 14:48:50 -0800108}
109
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700110// Atomically set an entry to drop
111// entry->setDropped(1) must follow this call, caller should do this explicitly.
112void LogStatistics::drop(LogBufferElement *e) {
113 log_id_t log_id = e->getLogId();
114 unsigned short size = e->getMsgLen();
115 mSizes[log_id] -= size;
116
Mark Salyzyn14d39b32015-04-13 14:24:45 -0700117 uidTable[log_id].drop(e->getUid(), e);
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700118
119 if (!enable) {
120 return;
121 }
122
Mark Salyzyn14d39b32015-04-13 14:24:45 -0700123 pidTable.drop(e->getPid(), e);
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700124}
125
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700126// caller must own and free character string
127char *LogStatistics::uidToName(uid_t uid) {
128 // Local hard coded favourites
129 if (uid == AID_LOGD) {
130 return strdup("auditd");
Mark Salyzynd774bce2014-02-06 14:48:50 -0800131 }
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700132
133 // Android hard coded
134 const struct android_id_info *info = android_ids;
135
136 for (size_t i = 0; i < android_id_count; ++i) {
137 if (info->aid == uid) {
138 return strdup(info->name);
139 }
140 ++info;
Mark Salyzynd774bce2014-02-06 14:48:50 -0800141 }
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700142
Mark Salyzyn9018bc92015-03-16 08:26:05 -0700143 // Parse /data/system/packages.list
Mark Salyzynf9908b02015-04-29 12:48:45 -0700144 uid_t userId = uid % AID_USER;
145 char *name = android::uidToName(userId);
146 if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
147 name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
148 }
Mark Salyzyn9018bc92015-03-16 08:26:05 -0700149 if (name) {
150 return name;
151 }
Mark Salyzyn1435b472015-03-16 08:26:05 -0700152
153 // report uid -> pid(s) -> pidToName if unique
154 ssize_t index = -1;
155 while ((index = pidTable.next(index)) != -1) {
156 const PidEntry &entry = pidTable.entryAt(index);
157
158 if (entry.getUid() == uid) {
159 const char *n = entry.getName();
160
161 if (n) {
162 if (!name) {
163 name = strdup(n);
164 } else if (strcmp(name, n)) {
165 free(name);
Mark Salyzynf9908b02015-04-29 12:48:45 -0700166 name = NULL;
167 break;
Mark Salyzyn1435b472015-03-16 08:26:05 -0700168 }
169 }
170 }
171 }
172
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700173 // No one
Mark Salyzyn1435b472015-03-16 08:26:05 -0700174 return name;
Mark Salyzynd774bce2014-02-06 14:48:50 -0800175}
176
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700177static void format_line(android::String8 &output,
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700178 android::String8 &name, android::String8 &size, android::String8 &pruned) {
179 static const size_t pruned_len = 6;
180 static const size_t total_len = 70 + pruned_len;
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700181
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700182 ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
183 ssize_t size_len = std::max(size.length() + 1,
184 total_len - name.length() - drop_len - 1);
185
186 if (pruned.length()) {
187 output.appendFormat("%s%*s%*s\n", name.string(),
188 (int)size_len, size.string(),
189 (int)drop_len, pruned.string());
190 } else {
191 output.appendFormat("%s%*s\n", name.string(),
192 (int)size_len, size.string());
193 }
Mark Salyzynd774bce2014-02-06 14:48:50 -0800194}
195
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700196void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
Mark Salyzyn5766f802014-04-07 07:05:40 -0700197 static const unsigned short spaces_total = 19;
Mark Salyzynd774bce2014-02-06 14:48:50 -0800198
199 if (*buf) {
Greg Hackmann2104c412014-04-06 21:25:58 -0700200 free(*buf);
Mark Salyzynd774bce2014-02-06 14:48:50 -0800201 *buf = NULL;
202 }
203
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700204 // Report on total logging, current and for all time
Mark Salyzynd774bce2014-02-06 14:48:50 -0800205
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700206 android::String8 output("size/num");
207 size_t oldLength;
208 short spaces = 1;
209
210 log_id_for_each(id) {
211 if (!(logMask & (1 << id))) {
Mark Salyzyn1867ca42014-04-04 16:35:59 -0700212 continue;
213 }
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700214 oldLength = output.length();
Mark Salyzyn1867ca42014-04-04 16:35:59 -0700215 if (spaces < 0) {
216 spaces = 0;
217 }
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700218 output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
219 spaces += spaces_total + oldLength - output.length();
Mark Salyzynd774bce2014-02-06 14:48:50 -0800220 }
221
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700222 spaces = 4;
223 output.appendFormat("\nTotal");
Mark Salyzynd774bce2014-02-06 14:48:50 -0800224
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700225 log_id_for_each(id) {
226 if (!(logMask & (1 << id))) {
227 continue;
Mark Salyzynd774bce2014-02-06 14:48:50 -0800228 }
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700229 oldLength = output.length();
230 if (spaces < 0) {
231 spaces = 0;
232 }
233 output.appendFormat("%*s%zu/%zu", spaces, "",
234 sizesTotal(id), elementsTotal(id));
235 spaces += spaces_total + oldLength - output.length();
Mark Salyzynd774bce2014-02-06 14:48:50 -0800236 }
237
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700238 spaces = 6;
239 output.appendFormat("\nNow");
Mark Salyzynd774bce2014-02-06 14:48:50 -0800240
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700241 log_id_for_each(id) {
242 if (!(logMask & (1 << id))) {
Mark Salyzynd774bce2014-02-06 14:48:50 -0800243 continue;
244 }
245
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700246 size_t els = elements(id);
Mark Salyzynd774bce2014-02-06 14:48:50 -0800247 if (els) {
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700248 oldLength = output.length();
Mark Salyzynfe639a02014-02-19 17:18:31 -0800249 if (spaces < 0) {
250 spaces = 0;
251 }
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700252 output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
253 spaces -= output.length() - oldLength;
Mark Salyzynd774bce2014-02-06 14:48:50 -0800254 }
255 spaces += spaces_total;
256 }
257
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700258 // Report on Chattiest
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700259
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700260 // Chattiest by application (UID)
Mark Salyzyn1435b472015-03-16 08:26:05 -0700261 static const size_t maximum_sorted_entries = 32;
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700262 log_id_for_each(id) {
263 if (!(logMask & (1 << id))) {
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700264 continue;
265 }
266
Mark Salyzyn1435b472015-03-16 08:26:05 -0700267 bool headerPrinted = false;
268 std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
269 ssize_t index = -1;
270 while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700271 const UidEntry *entry = sorted[index];
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700272 uid_t u = entry->getKey();
273 if ((uid != AID_ROOT) && (u != uid)) {
274 continue;
275 }
276
Mark Salyzyn1435b472015-03-16 08:26:05 -0700277 if (!headerPrinted) {
Mark Salyzyn057b7442015-04-21 07:43:16 -0700278 output.appendFormat("\n\n");
279 android::String8 name("");
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700280 if (uid == AID_ROOT) {
Mark Salyzyn057b7442015-04-21 07:43:16 -0700281 name.appendFormat(
282 "Chattiest UIDs in %s log buffer:",
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700283 android_log_id_to_name(id));
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700284 } else {
Mark Salyzyn057b7442015-04-21 07:43:16 -0700285 name.appendFormat(
286 "Logging for your UID in %s log buffer:",
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700287 android_log_id_to_name(id));
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700288 }
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700289 android::String8 size("Size");
290 android::String8 pruned("Pruned");
Mark Salyzyn0e765c62015-03-17 17:17:25 -0700291 if (!worstUidEnabledForLogid(id)) {
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700292 pruned.setTo("");
293 }
294 format_line(output, name, size, pruned);
Mark Salyzyn057b7442015-04-21 07:43:16 -0700295
296 name.setTo("UID PACKAGE");
297 size.setTo("BYTES");
298 pruned.setTo("LINES");
299 if (!worstUidEnabledForLogid(id)) {
300 pruned.setTo("");
301 }
302 format_line(output, name, size, pruned);
303
Mark Salyzyn1435b472015-03-16 08:26:05 -0700304 headerPrinted = true;
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700305 }
306
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700307 android::String8 name("");
308 name.appendFormat("%u", u);
309 char *n = uidToName(u);
310 if (n) {
311 name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
312 free(n);
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700313 }
314
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700315 android::String8 size("");
Mark Salyzyn1435b472015-03-16 08:26:05 -0700316 size.appendFormat("%zu", entry->getSizes());
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700317
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700318 android::String8 pruned("");
319 size_t dropped = entry->getDropped();
320 if (dropped) {
321 pruned.appendFormat("%zu", dropped);
322 }
323
324 format_line(output, name, size, pruned);
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700325 }
Mark Salyzyn1435b472015-03-16 08:26:05 -0700326 }
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700327
Mark Salyzyn1435b472015-03-16 08:26:05 -0700328 if (enable) {
Mark Salyzyn14d39b32015-04-13 14:24:45 -0700329 // Pid table
Mark Salyzyn1435b472015-03-16 08:26:05 -0700330 bool headerPrinted = false;
331 std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
332 ssize_t index = -1;
333 while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
334 const PidEntry *entry = sorted[index];
335 uid_t u = entry->getUid();
336 if ((uid != AID_ROOT) && (u != uid)) {
337 continue;
338 }
339
340 if (!headerPrinted) {
Mark Salyzyn057b7442015-04-21 07:43:16 -0700341 output.appendFormat("\n\n");
342 android::String8 name("");
Mark Salyzyn1435b472015-03-16 08:26:05 -0700343 if (uid == AID_ROOT) {
Mark Salyzyn057b7442015-04-21 07:43:16 -0700344 name.appendFormat("Chattiest PIDs:");
Mark Salyzyn1435b472015-03-16 08:26:05 -0700345 } else {
Mark Salyzyn057b7442015-04-21 07:43:16 -0700346 name.appendFormat("Logging for this PID:");
Mark Salyzyn1435b472015-03-16 08:26:05 -0700347 }
Mark Salyzyn1435b472015-03-16 08:26:05 -0700348 android::String8 size("Size");
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700349 android::String8 pruned("Pruned");
350 format_line(output, name, size, pruned);
Mark Salyzyn057b7442015-04-21 07:43:16 -0700351
352 name.setTo(" PID/UID COMMAND LINE");
353 size.setTo("BYTES");
354 pruned.setTo("LINES");
355 format_line(output, name, size, pruned);
356
Mark Salyzyn1435b472015-03-16 08:26:05 -0700357 headerPrinted = true;
358 }
359
360 android::String8 name("");
361 name.appendFormat("%5u/%u", entry->getKey(), u);
362 const char *n = entry->getName();
363 if (n) {
364 name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
365 } else {
366 char *un = uidToName(u);
367 if (un) {
368 name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
369 free(un);
370 }
371 }
372
373 android::String8 size("");
374 size.appendFormat("%zu", entry->getSizes());
375
Mark Salyzyna69d5a42015-03-16 12:04:09 -0700376 android::String8 pruned("");
377 size_t dropped = entry->getDropped();
378 if (dropped) {
379 pruned.appendFormat("%zu", dropped);
380 }
381
382 format_line(output, name, size, pruned);
Mark Salyzyn1435b472015-03-16 08:26:05 -0700383 }
Mark Salyzyn692e3c32014-03-26 10:46:39 -0700384 }
385
Mark Salyzynf96ac902015-04-13 14:24:45 -0700386 if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
387 // Tag table
388 bool headerPrinted = false;
389 std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries);
390 ssize_t index = -1;
391 while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
392 const TagEntry *entry = sorted[index];
393 uid_t u = entry->getUid();
394 if ((uid != AID_ROOT) && (u != uid)) {
395 continue;
396 }
397
398 android::String8 pruned("");
399
400 if (!headerPrinted) {
401 output.appendFormat("\n\n");
402 android::String8 name("Chattiest events log buffer TAGs:");
403 android::String8 size("Size");
404 format_line(output, name, size, pruned);
405
406 name.setTo(" TAG/UID TAGNAME");
407 size.setTo("BYTES");
408 format_line(output, name, size, pruned);
409
410 headerPrinted = true;
411 }
412
413 android::String8 name("");
414 if (u == (uid_t)-1) {
415 name.appendFormat("%7u", entry->getKey());
416 } else {
417 name.appendFormat("%7u/%u", entry->getKey(), u);
418 }
419 const char *n = entry->getName();
420 if (n) {
421 name.appendFormat("%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", n);
422 }
423
424 android::String8 size("");
425 size.appendFormat("%zu", entry->getSizes());
426
427 format_line(output, name, size, pruned);
428 }
429 }
430
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700431 *buf = strdup(output.string());
Mark Salyzynd774bce2014-02-06 14:48:50 -0800432}
Mark Salyzynf67c3792014-04-07 07:15:33 -0700433
Mark Salyzyn1435b472015-03-16 08:26:05 -0700434namespace android {
435
436uid_t pidToUid(pid_t pid) {
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700437 char buffer[512];
438 snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
439 FILE *fp = fopen(buffer, "r");
440 if (fp) {
441 while (fgets(buffer, sizeof(buffer), fp)) {
442 int uid;
Mark Salyzyn8f5f8962015-04-14 13:07:29 -0700443 if (sscanf(buffer, "Uid: %d", &uid) == 1) {
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700444 fclose(fp);
445 return uid;
Mark Salyzynf67c3792014-04-07 07:15:33 -0700446 }
447 }
Mark Salyzyn9cf5d402015-03-10 13:51:35 -0700448 fclose(fp);
Mark Salyzynf67c3792014-04-07 07:15:33 -0700449 }
Mark Salyzynb4853952015-03-17 07:56:32 -0700450 return AID_LOGD; // associate this with the logger
Mark Salyzynf67c3792014-04-07 07:15:33 -0700451}
Mark Salyzyn1435b472015-03-16 08:26:05 -0700452
453}
454
455uid_t LogStatistics::pidToUid(pid_t pid) {
Mark Salyzyn14d39b32015-04-13 14:24:45 -0700456 return pidTable.entryAt(pidTable.add(pid)).getUid();
Mark Salyzyn1435b472015-03-16 08:26:05 -0700457}
458
459// caller must free character string
460char *LogStatistics::pidToName(pid_t pid) {
Mark Salyzyn14d39b32015-04-13 14:24:45 -0700461 const char *name = pidTable.entryAt(pidTable.add(pid)).getName();
462 if (!name) {
463 return NULL;
Mark Salyzyn1435b472015-03-16 08:26:05 -0700464 }
Mark Salyzyn14d39b32015-04-13 14:24:45 -0700465 return strdup(name);
Mark Salyzyn1435b472015-03-16 08:26:05 -0700466}