Remove liblog, logcat, logd, logwrapper
These subdirectories have moved to platform/system/logging. BUG: 168791309 Test: Local build + TH Change-Id: Iaee2ff59d4450f3e59dc9ea8b0e257b2de53e478
This commit is contained in:
parent
83e9bc346a
commit
d2c21a10d3
|
@ -1 +0,0 @@
|
|||
../.clang-format-2
|
|
@ -1,156 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2008-2014 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
liblog_sources = [
|
||||
"log_event_list.cpp",
|
||||
"log_event_write.cpp",
|
||||
"logger_name.cpp",
|
||||
"logger_read.cpp",
|
||||
"logger_write.cpp",
|
||||
"logprint.cpp",
|
||||
"properties.cpp",
|
||||
]
|
||||
liblog_target_sources = [
|
||||
"event_tag_map.cpp",
|
||||
"log_time.cpp",
|
||||
"pmsg_reader.cpp",
|
||||
"pmsg_writer.cpp",
|
||||
"logd_reader.cpp",
|
||||
"logd_writer.cpp",
|
||||
]
|
||||
|
||||
cc_library_headers {
|
||||
name: "liblog_headers",
|
||||
host_supported: true,
|
||||
vendor_available: true,
|
||||
ramdisk_available: true,
|
||||
recovery_available: true,
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"//apex_available:anyapex",
|
||||
],
|
||||
min_sdk_version: "29",
|
||||
sdk_version: "minimum",
|
||||
native_bridge_supported: true,
|
||||
export_include_dirs: ["include"],
|
||||
system_shared_libs: [],
|
||||
stl: "none",
|
||||
target: {
|
||||
windows: {
|
||||
enabled: true,
|
||||
},
|
||||
linux_bionic: {
|
||||
enabled: true,
|
||||
},
|
||||
vendor: {
|
||||
override_export_include_dirs: ["include_vndk"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Shared and static library for host and device
|
||||
// ========================================================
|
||||
cc_library {
|
||||
name: "liblog",
|
||||
host_supported: true,
|
||||
ramdisk_available: true,
|
||||
recovery_available: true,
|
||||
native_bridge_supported: true,
|
||||
srcs: liblog_sources,
|
||||
|
||||
target: {
|
||||
android: {
|
||||
version_script: "liblog.map.txt",
|
||||
srcs: liblog_target_sources,
|
||||
// AddressSanitizer runtime library depends on liblog.
|
||||
sanitize: {
|
||||
address: false,
|
||||
},
|
||||
},
|
||||
android_arm: {
|
||||
// TODO: This is to work around b/24465209. Remove after root cause is fixed
|
||||
pack_relocations: false,
|
||||
ldflags: ["-Wl,--hash-style=both"],
|
||||
},
|
||||
windows: {
|
||||
enabled: true,
|
||||
},
|
||||
not_windows: {
|
||||
srcs: ["event_tag_map.cpp"],
|
||||
},
|
||||
linux_bionic: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
|
||||
header_libs: [
|
||||
"libbase_headers",
|
||||
"libcutils_headers",
|
||||
"liblog_headers",
|
||||
],
|
||||
export_header_lib_headers: ["liblog_headers"],
|
||||
|
||||
stubs: {
|
||||
symbol_file: "liblog.map.txt",
|
||||
versions: ["29", "30"],
|
||||
},
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
// This is what we want to do:
|
||||
// liblog_cflags := $(shell \
|
||||
// sed -n \
|
||||
// 's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
|
||||
// $(LOCAL_PATH)/event.logtags)
|
||||
// so make sure we do not regret hard-coding it as follows:
|
||||
"-DLIBLOG_LOG_TAG=1006",
|
||||
"-DSNET_EVENT_LOG_TAG=1397638484",
|
||||
],
|
||||
logtags: ["event.logtags"],
|
||||
compile_multilib: "both",
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
// liblog is exceptionally available to the runtime APEX
|
||||
// because the dynamic linker has to use it statically.
|
||||
// See b/151051671
|
||||
"com.android.runtime",
|
||||
// DO NOT add more apex names here
|
||||
],
|
||||
}
|
||||
|
||||
ndk_headers {
|
||||
name: "liblog_ndk_headers",
|
||||
from: "include/android",
|
||||
to: "android",
|
||||
srcs: ["include/android/log.h"],
|
||||
license: "NOTICE",
|
||||
}
|
||||
|
||||
ndk_library {
|
||||
name: "liblog",
|
||||
symbol_file: "liblog.map.txt",
|
||||
first_version: "9",
|
||||
unversioned_until: "current",
|
||||
}
|
||||
|
||||
llndk_library {
|
||||
name: "liblog",
|
||||
native_bridge_supported: true,
|
||||
symbol_file: "liblog.map.txt",
|
||||
export_include_dirs: ["include_vndk"],
|
||||
}
|
190
liblog/NOTICE
190
liblog/NOTICE
|
@ -1,190 +0,0 @@
|
|||
|
||||
Copyright (c) 2005-2014, The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
|
@ -1 +0,0 @@
|
|||
tomcherry@google.com
|
161
liblog/README.md
161
liblog/README.md
|
@ -1,161 +0,0 @@
|
|||
Android liblog
|
||||
--------------
|
||||
|
||||
Public Functions and Macros
|
||||
---------------------------
|
||||
|
||||
/*
|
||||
* Please limit to 24 characters for runtime is loggable,
|
||||
* 16 characters for persist is loggable, and logcat pretty
|
||||
* alignment with limit of 7 characters.
|
||||
*/
|
||||
#define LOG_TAG "yourtag"
|
||||
#include <log/log.h>
|
||||
|
||||
ALOG(android_priority, tag, format, ...)
|
||||
IF_ALOG(android_priority, tag)
|
||||
LOG_PRI(priority, tag, format, ...)
|
||||
LOG_PRI_VA(priority, tag, format, args)
|
||||
#define LOG_TAG NULL
|
||||
ALOGV(format, ...)
|
||||
SLOGV(format, ...)
|
||||
RLOGV(format, ...)
|
||||
ALOGV_IF(cond, format, ...)
|
||||
SLOGV_IF(cond, format, ...)
|
||||
RLOGV_IF(cond, format, ...)
|
||||
IF_ALOGC()
|
||||
ALOGD(format, ...)
|
||||
SLOGD(format, ...)
|
||||
RLOGD(format, ...)
|
||||
ALOGD_IF(cond, format, ...)
|
||||
SLOGD_IF(cond, format, ...)
|
||||
RLOGD_IF(cond, format, ...)
|
||||
IF_ALOGD()
|
||||
ALOGI(format, ...)
|
||||
SLOGI(format, ...)
|
||||
RLOGI(format, ...)
|
||||
ALOGI_IF(cond, format, ...)
|
||||
SLOGI_IF(cond, format, ...)
|
||||
RLOGI_IF(cond, format, ...)
|
||||
IF_ALOGI()
|
||||
ALOGW(format, ...)
|
||||
SLOGW(format, ...)
|
||||
RLOGW(format, ...)
|
||||
ALOGW_IF(cond, format, ...)
|
||||
SLOGW_IF(cond, format, ...)
|
||||
RLOGW_IF(cond, format, ...)
|
||||
IF_ALOGW()
|
||||
ALOGE(format, ...)
|
||||
SLOGE(format, ...)
|
||||
RLOGE(format, ...)
|
||||
ALOGE_IF(cond, format, ...)
|
||||
SLOGE_IF(cond, format, ...)
|
||||
RLOGE_IF(cond, format, ...)
|
||||
IF_ALOGE()
|
||||
LOG_FATAL(format, ...)
|
||||
LOG_ALWAYS_FATAL(format, ...)
|
||||
LOG_FATAL_IF(cond, format, ...)
|
||||
LOG_ALWAYS_FATAL_IF(cond, format, ...)
|
||||
ALOG_ASSERT(cond, format, ...)
|
||||
LOG_EVENT_INT(tag, value)
|
||||
LOG_EVENT_LONG(tag, value)
|
||||
|
||||
log_id_t android_logger_get_id(struct logger *logger)
|
||||
int android_logger_clear(struct logger *logger)
|
||||
int android_logger_get_log_size(struct logger *logger)
|
||||
int android_logger_get_log_readable_size(struct logger *logger)
|
||||
int android_logger_get_log_version(struct logger *logger)
|
||||
|
||||
struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid)
|
||||
struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id)
|
||||
struct logger_list *android_logger_list_open(log_id_t id, int mode, unsigned int tail, pid_t pid)
|
||||
int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg)
|
||||
void android_logger_list_free(struct logger_list *logger_list)
|
||||
|
||||
log_id_t android_name_to_log_id(const char *logName)
|
||||
const char *android_log_id_to_name(log_id_t log_id)
|
||||
|
||||
android_log_context create_android_logger(uint32_t tag)
|
||||
|
||||
int android_log_write_list_begin(android_log_context ctx)
|
||||
int android_log_write_list_end(android_log_context ctx)
|
||||
|
||||
int android_log_write_int32(android_log_context ctx, int32_t value)
|
||||
int android_log_write_int64(android_log_context ctx, int64_t value)
|
||||
int android_log_write_string8(android_log_context ctx, const char *value)
|
||||
int android_log_write_string8_len(android_log_context ctx, const char *value, size_t maxlen)
|
||||
int android_log_write_float32(android_log_context ctx, float value)
|
||||
|
||||
int android_log_write_list(android_log_context ctx, log_id_t id = LOG_ID_EVENTS)
|
||||
|
||||
android_log_context create_android_log_parser(const char *msg, size_t len)
|
||||
android_log_list_element android_log_read_next(android_log_context ctx)
|
||||
android_log_list_element android_log_peek_next(android_log_context ctx)
|
||||
|
||||
int android_log_destroy(android_log_context *ctx)
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
liblog represents an interface to the volatile Android Logging system for NDK (Native) applications
|
||||
and libraries. Interfaces for either writing or reading logs. The log buffers are divided up in
|
||||
Main, System, Radio and Events sub-logs.
|
||||
|
||||
The logging interfaces are a series of macros, all of which can be overridden individually in order
|
||||
to control the verbosity of the application or library. `[ASR]LOG[VDIWE]` calls are used to log to
|
||||
BAsic, System or Radio sub-logs in either the Verbose, Debug, Info, Warning or Error priorities.
|
||||
`[ASR]LOG[VDIWE]_IF` calls are used to perform thus based on a condition being true.
|
||||
`IF_ALOG[VDIWE]` calls are true if the current `LOG_TAG` is enabled at the specified priority.
|
||||
`LOG_ALWAYS_FATAL` is used to `ALOG` a message, then kill the process. `LOG_FATAL` call is a
|
||||
variant of `LOG_ALWAYS_FATAL`, only enabled in engineering, and not release builds. `ALOG_ASSERT`
|
||||
is used to `ALOG` a message if the condition is false; the condition is part of the logged message.
|
||||
`LOG_EVENT_(INT|LONG)` is used to drop binary content into the Events sub-log.
|
||||
|
||||
The log reading interfaces permit opening the logs either singly or multiply, retrieving a log entry
|
||||
at a time in time sorted order, optionally limited to a specific pid and tail of the log(s) and
|
||||
finally a call closing the logs. A single log can be opened with `android_logger_list_open()`; or
|
||||
multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
|
||||
`android_logger_open()` for each log id. Each entry can be retrieved with
|
||||
`android_logger_list_read()`. The log(s) can be closed with `android_logger_list_free()`.
|
||||
`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return
|
||||
code, otherwise the `android_logger_list_read()` call will block for new entries.
|
||||
|
||||
The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
|
||||
the reader until the buffer is about to prune at the start time then proceed to dumping content.
|
||||
|
||||
The `ANDROID_LOG_PSTORE` mode flag to the `android_logger_open()` is used to switch from the active
|
||||
logs to the persistent logs from before the last reboot.
|
||||
|
||||
The value returned by `android_logger_open()` can be used as a parameter to the
|
||||
`android_logger_clear()` function to empty the sub-log.
|
||||
|
||||
The value returned by `android_logger_open()` can be used as a parameter to the
|
||||
`android_logger_get_log_(size|readable_size|version)` to retrieve the sub-log maximum size, readable
|
||||
size and log buffer format protocol version respectively. `android_logger_get_id()` returns the id
|
||||
that was used when opening the sub-log.
|
||||
|
||||
Errors
|
||||
------
|
||||
|
||||
If messages fail, a negative error code will be returned to the caller.
|
||||
|
||||
The `-ENOTCONN` return code indicates that the logger daemon is stopped.
|
||||
|
||||
The `-EBADF` return code indicates that the log access point can not be opened, or the log buffer id
|
||||
is out of range.
|
||||
|
||||
For the `-EAGAIN` return code, this means that the logging message was temporarily backed-up either
|
||||
because of Denial Of Service (DOS) logging pressure from some chatty application or service in the
|
||||
Android system, or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen. To aid in
|
||||
diagnosing the occurence of this, a binary event from liblog will be sent to the log daemon once a
|
||||
new message can get through indicating how many messages were dropped as a result. Please take
|
||||
action to resolve the structural problems at the source.
|
||||
|
||||
It is generally not advised for the caller to retry the `-EAGAIN` return code as this will only make
|
||||
the problem(s) worse and cause your application to temporarily drop to the logger daemon priority,
|
||||
BATCH scheduling policy and background task cgroup. If you require a group of messages to be passed
|
||||
atomically, merge them into one message with embedded newlines to the maximum length
|
||||
`LOGGER_ENTRY_MAX_PAYLOAD`.
|
||||
|
||||
Other return codes from writing operation can be returned. Since the library retries on `EINTR`,
|
||||
`-EINTR` should never be returned.
|
|
@ -1,92 +0,0 @@
|
|||
# liblog -> logd
|
||||
|
||||
The data that liblog sends to logd is represented below.
|
||||
|
||||
struct {
|
||||
android_log_header_t header;
|
||||
union {
|
||||
struct {
|
||||
char prio;
|
||||
char tag[...];
|
||||
char message[...];
|
||||
} string;
|
||||
struct {
|
||||
android_event_header_t event_header;
|
||||
android_event_*_t payload[...];
|
||||
} binary;
|
||||
};
|
||||
};
|
||||
|
||||
where the embedded structs are defined as:
|
||||
|
||||
struct android_log_header_t {
|
||||
uint8_t id;
|
||||
uint16_t tid;
|
||||
log_time realtime;
|
||||
};
|
||||
|
||||
struct log_time {
|
||||
uint32_t tv_sec = 0;
|
||||
uint32_t tv_nsec = 0;
|
||||
}
|
||||
|
||||
struct android_event_header_t {
|
||||
int32_t tag;
|
||||
};
|
||||
|
||||
struct android_event_list_t {
|
||||
int8_t type; // EVENT_TYPE_LIST
|
||||
int8_t element_count;
|
||||
};
|
||||
|
||||
struct android_event_float_t {
|
||||
int8_t type; // EVENT_TYPE_FLOAT
|
||||
float data;
|
||||
};
|
||||
|
||||
struct android_event_int_t {
|
||||
int8_t type; // EVENT_TYPE_INT
|
||||
int32_t data;
|
||||
} android_event_int_t;
|
||||
|
||||
struct android_event_long_t {
|
||||
int8_t type; // EVENT_TYPE_LONG
|
||||
int64_t data;
|
||||
};
|
||||
|
||||
struct android_event_string_t {
|
||||
int8_t type; // EVENT_TYPE_STRING;
|
||||
int32_t length;
|
||||
char data[];
|
||||
};
|
||||
|
||||
The payload, excluding the header, has a max size of LOGGER_ENTRY_MAX_PAYLOAD.
|
||||
|
||||
## header
|
||||
|
||||
The header is added immediately before sending the log message to logd.
|
||||
|
||||
## `string` payload
|
||||
|
||||
The `string` part of the union is for normal buffers (main, system, radio, etc) and consists of a
|
||||
single character priority, followed by a variable length null terminated string for the tag, and
|
||||
finally a variable length null terminated string for the message.
|
||||
|
||||
This payload is used for the `__android_log_buf_write()` family of functions.
|
||||
|
||||
## `binary` payload
|
||||
|
||||
The `binary` part of the union is for binary buffers (events, security, etc) and consists of an
|
||||
android_event_header_t struct followed by a variable number of android_event_*_t
|
||||
(android_event_list_t, android_event_int_t, etc) structs.
|
||||
|
||||
If multiple android_event_*_t elements are present, then they must be in a list and the first
|
||||
element in payload must be an android_event_list_t.
|
||||
|
||||
This payload is used for the `__android_log_bwrite()` family of functions. It is additionally used
|
||||
for `android_log_write_list()` and the related functions that manipulate event lists.
|
||||
|
||||
# logd -> liblog
|
||||
|
||||
logd sends a `logger_entry` struct to liblog followed by the payload. The payload is identical to
|
||||
the payloads defined above. The max size of the entire message from logd is LOGGER_ENTRY_MAX_LEN.
|
|
@ -1,37 +0,0 @@
|
|||
# The entries in this file map a sparse set of log tag numbers to tag names.
|
||||
# This is installed on the device, in /system/etc, and parsed by logcat.
|
||||
#
|
||||
# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
|
||||
# negative values alone for now.)
|
||||
#
|
||||
# Tag names are one or more ASCII letters and numbers or underscores, i.e.
|
||||
# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
|
||||
# impacts log readability, the latter makes regex searches more annoying).
|
||||
#
|
||||
# Tag numbers and names are separated by whitespace. Blank lines and lines
|
||||
# starting with '#' are ignored.
|
||||
#
|
||||
# Optionally, after the tag names can be put a description for the value(s)
|
||||
# of the tag. Description are in the format
|
||||
# (<name>|data type[|data unit])
|
||||
# Multiple values are separated by commas.
|
||||
#
|
||||
# The data type is a number from the following values:
|
||||
# 1: int
|
||||
# 2: long
|
||||
# 3: string
|
||||
# 4: list
|
||||
#
|
||||
# The data unit is a number taken from the following list:
|
||||
# 1: Number of objects
|
||||
# 2: Number of bytes
|
||||
# 3: Number of milliseconds
|
||||
# 4: Number of allocations
|
||||
# 5: Id
|
||||
# 6: Percent
|
||||
# s: Number of seconds (monotonic time)
|
||||
# Default value for data of type int/long is 2 (bytes).
|
||||
#
|
||||
# TODO: generate ".java" and ".h" files with integer constants from this file.
|
||||
|
||||
1006 liblog (dropped|1)
|
|
@ -1,384 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <log/event_tag_map.h>
|
||||
#include <private/android_logger.h>
|
||||
#include <utils/FastStrcmp.h>
|
||||
#include <utils/RWLock.h>
|
||||
|
||||
#define OUT_TAG "EventTagMap"
|
||||
|
||||
typedef std::pair<std::string_view, std::string_view> TagFmt;
|
||||
|
||||
// Map
|
||||
struct EventTagMap {
|
||||
#define NUM_MAPS 2
|
||||
// memory-mapped source file; we get strings from here
|
||||
void* mapAddr[NUM_MAPS];
|
||||
size_t mapLen[NUM_MAPS];
|
||||
|
||||
private:
|
||||
std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
|
||||
std::unordered_map<std::string_view, uint32_t> Tag2Idx;
|
||||
// protect unordered sets
|
||||
android::RWLock rwlock;
|
||||
|
||||
public:
|
||||
EventTagMap() {
|
||||
memset(mapAddr, 0, sizeof(mapAddr));
|
||||
memset(mapLen, 0, sizeof(mapLen));
|
||||
}
|
||||
|
||||
~EventTagMap() {
|
||||
Idx2TagFmt.clear();
|
||||
Tag2Idx.clear();
|
||||
for (size_t which = 0; which < NUM_MAPS; ++which) {
|
||||
if (mapAddr[which]) {
|
||||
munmap(mapAddr[which], mapLen[which]);
|
||||
mapAddr[which] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
|
||||
const TagFmt* find(uint32_t tag) const;
|
||||
int find(std::string_view tag) const;
|
||||
};
|
||||
|
||||
bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
|
||||
bool verbose) {
|
||||
bool ret = true;
|
||||
static const char errorFormat[] =
|
||||
OUT_TAG ": duplicate tag entries %" PRIu32 ":%.*s:%.*s and %" PRIu32
|
||||
":%.*s:%.*s)\n";
|
||||
android::RWLock::AutoWLock writeLock(rwlock);
|
||||
{
|
||||
auto it = Idx2TagFmt.find(tag);
|
||||
if (it != Idx2TagFmt.end()) {
|
||||
if (verbose) {
|
||||
fprintf(stderr, errorFormat, it->first, (int)it->second.first.length(),
|
||||
it->second.first.data(), (int)it->second.second.length(),
|
||||
it->second.second.data(), tag, (int)tagfmt.first.length(),
|
||||
tagfmt.first.data(), (int)tagfmt.second.length(),
|
||||
tagfmt.second.data());
|
||||
}
|
||||
ret = false;
|
||||
} else {
|
||||
Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto it = Tag2Idx.find(tagfmt.first);
|
||||
if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
|
||||
Tag2Idx.erase(it);
|
||||
it = Tag2Idx.end();
|
||||
}
|
||||
if (it == Tag2Idx.end()) {
|
||||
Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const TagFmt* EventTagMap::find(uint32_t tag) const {
|
||||
android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
|
||||
auto it = Idx2TagFmt.find(tag);
|
||||
if (it == Idx2TagFmt.end()) return NULL;
|
||||
return &(it->second);
|
||||
}
|
||||
|
||||
int EventTagMap::find(std::string_view tag) const {
|
||||
android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
|
||||
auto it = Tag2Idx.find(std::move(tag));
|
||||
if (it == Tag2Idx.end()) return -1;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// The position after the end of a valid section of the tag string,
|
||||
// caller makes sure delimited appropriately.
|
||||
static const char* endOfTag(const char* cp) {
|
||||
while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
// Scan one tag line.
|
||||
//
|
||||
// "pData" should be pointing to the first digit in the tag number. On
|
||||
// successful return, it will be pointing to the last character in the
|
||||
// tag line (i.e. the character before the start of the next line).
|
||||
//
|
||||
// Returns 0 on success, nonzero on failure.
|
||||
static int scanTagLine(EventTagMap* map, const char*& pData, int line_num) {
|
||||
char* ep;
|
||||
unsigned long val = strtoul(pData, &ep, 10);
|
||||
const char* cp = ep;
|
||||
if (cp == pData) {
|
||||
fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", line_num);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t tagIndex = val;
|
||||
if (tagIndex != val) {
|
||||
fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", line_num);
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((*++cp != '\n') && isspace(*cp)) {
|
||||
}
|
||||
|
||||
if (*cp == '\n') {
|
||||
fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", line_num);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* tag = cp;
|
||||
cp = endOfTag(cp);
|
||||
size_t tagLen = cp - tag;
|
||||
|
||||
if (!isspace(*cp)) {
|
||||
fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp, line_num);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (isspace(*cp) && (*cp != '\n')) ++cp;
|
||||
const char* fmt = NULL;
|
||||
size_t fmtLen = 0;
|
||||
if (*cp && (*cp != '#')) {
|
||||
fmt = cp;
|
||||
while (*cp && (*cp != '\n') && (*cp != '#')) ++cp;
|
||||
while ((cp > fmt) && isspace(*(cp - 1))) --cp;
|
||||
fmtLen = cp - fmt;
|
||||
}
|
||||
|
||||
// KISS Only report identicals if they are global
|
||||
// Ideally we want to check if there are identicals
|
||||
// recorded for the same uid, but recording that
|
||||
// unused detail in our database is too burdensome.
|
||||
bool verbose = true;
|
||||
while (*cp && (*cp != '#') && (*cp != '\n')) ++cp;
|
||||
if (*cp == '#') {
|
||||
do {
|
||||
++cp;
|
||||
} while (isspace(*cp) && (*cp != '\n'));
|
||||
verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
|
||||
}
|
||||
|
||||
while (*cp && (*cp != '\n')) ++cp;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%d: %p: %.*s\n", line_num, tag, (int)(cp - pData), pData);
|
||||
#endif
|
||||
pData = cp;
|
||||
|
||||
if (map->emplaceUnique(
|
||||
tagIndex,
|
||||
TagFmt(std::make_pair(std::string_view(tag, tagLen), std::string_view(fmt, fmtLen))),
|
||||
verbose)) {
|
||||
return 0;
|
||||
}
|
||||
errno = EMLINK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char* eventTagFiles[NUM_MAPS] = {
|
||||
EVENT_TAG_MAP_FILE, "/dev/event-log-tags",
|
||||
};
|
||||
|
||||
// Parse the tags out of the file.
|
||||
static int parseMapLines(EventTagMap* map, size_t which) {
|
||||
const char* cp = static_cast<char*>(map->mapAddr[which]);
|
||||
size_t len = map->mapLen[which];
|
||||
const char* endp = cp + len;
|
||||
|
||||
// insist on EOL at EOF; simplifies parsing and null-termination
|
||||
if (!len || (*(endp - 1) != '\n')) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
|
||||
which, len);
|
||||
#endif
|
||||
if (which) { // do not propagate errors for other files
|
||||
return 0;
|
||||
}
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool lineStart = true;
|
||||
int lineNum = 1;
|
||||
while (cp < endp) {
|
||||
if (*cp == '\n') {
|
||||
lineStart = true;
|
||||
lineNum++;
|
||||
} else if (lineStart) {
|
||||
if (*cp == '#') {
|
||||
// comment; just scan to end
|
||||
lineStart = false;
|
||||
} else if (isdigit(*cp)) {
|
||||
// looks like a tag; scan it out
|
||||
if (scanTagLine(map, cp, lineNum) != 0) {
|
||||
if (!which || (errno != EMLINK)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
lineNum++; // we eat the '\n'
|
||||
// leave lineStart==true
|
||||
} else if (isspace(*cp)) {
|
||||
// looks like leading whitespace; keep scanning
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
OUT_TAG
|
||||
": unexpected chars (0x%02x) in tag number on line %d\n",
|
||||
*cp, lineNum);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// this is a blank or comment line
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Open the map file and allocate a structure to manage it.
|
||||
//
|
||||
// We create a private mapping because we want to terminate the log tag
|
||||
// strings with '\0'.
|
||||
EventTagMap* android_openEventTagMap(const char* fileName) {
|
||||
EventTagMap* newTagMap;
|
||||
off_t end[NUM_MAPS];
|
||||
int save_errno, fd[NUM_MAPS];
|
||||
size_t which;
|
||||
|
||||
memset(fd, -1, sizeof(fd));
|
||||
memset(end, 0, sizeof(end));
|
||||
|
||||
for (which = 0; which < NUM_MAPS; ++which) {
|
||||
const char* tagfile = fileName ? fileName : eventTagFiles[which];
|
||||
|
||||
fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
|
||||
if (fd[which] < 0) {
|
||||
if (!which) {
|
||||
save_errno = errno;
|
||||
fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n", tagfile,
|
||||
strerror(save_errno));
|
||||
goto fail_errno;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
end[which] = lseek(fd[which], 0L, SEEK_END);
|
||||
save_errno = errno;
|
||||
(void)lseek(fd[which], 0L, SEEK_SET);
|
||||
if (!which && (end[0] < 0)) {
|
||||
fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n", tagfile,
|
||||
strerror(save_errno));
|
||||
goto fail_close;
|
||||
}
|
||||
if (fileName) break; // Only allow one as specified
|
||||
}
|
||||
|
||||
newTagMap = new EventTagMap;
|
||||
if (newTagMap == NULL) {
|
||||
save_errno = errno;
|
||||
goto fail_close;
|
||||
}
|
||||
|
||||
for (which = 0; which < NUM_MAPS; ++which) {
|
||||
if (fd[which] >= 0) {
|
||||
newTagMap->mapAddr[which] =
|
||||
mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
|
||||
which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
|
||||
save_errno = errno;
|
||||
close(fd[which]); /* fd DONE */
|
||||
fd[which] = -1;
|
||||
if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
|
||||
(newTagMap->mapAddr[which] != NULL)) {
|
||||
newTagMap->mapLen[which] = end[which];
|
||||
} else if (!which) {
|
||||
const char* tagfile = fileName ? fileName : eventTagFiles[which];
|
||||
|
||||
fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n", tagfile,
|
||||
strerror(save_errno));
|
||||
goto fail_unmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (which = 0; which < NUM_MAPS; ++which) {
|
||||
if (parseMapLines(newTagMap, which) != 0) {
|
||||
delete newTagMap;
|
||||
return NULL;
|
||||
}
|
||||
/* See 'fd DONE' comments above and below, no need to clean up here */
|
||||
}
|
||||
|
||||
return newTagMap;
|
||||
|
||||
fail_unmap:
|
||||
save_errno = EINVAL;
|
||||
delete newTagMap;
|
||||
fail_close:
|
||||
for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
|
||||
fail_errno:
|
||||
errno = save_errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Close the map.
|
||||
void android_closeEventTagMap(EventTagMap* map) {
|
||||
if (map) delete map;
|
||||
}
|
||||
|
||||
// Look up an entry in the map.
|
||||
const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
|
||||
if (len) *len = 0;
|
||||
const TagFmt* str = map->find(tag);
|
||||
if (!str) return NULL;
|
||||
if (len) *len = str->first.length();
|
||||
return str->first.data();
|
||||
}
|
||||
|
||||
// Look up an entry in the map.
|
||||
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
|
||||
if (len) *len = 0;
|
||||
const TagFmt* str = map->find(tag);
|
||||
if (!str) return NULL;
|
||||
if (len) *len = str->second.length();
|
||||
return str->second.data();
|
||||
}
|
||||
|
|
@ -1,380 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @addtogroup Logging
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Support routines to send messages to the Android log buffer,
|
||||
* which can later be accessed through the `logcat` utility.
|
||||
*
|
||||
* Each log message must have
|
||||
* - a priority
|
||||
* - a log tag
|
||||
* - some text
|
||||
*
|
||||
* The tag normally corresponds to the component that emits the log message,
|
||||
* and should be reasonably small.
|
||||
*
|
||||
* Log message text may be truncated to less than an implementation-specific
|
||||
* limit (1023 bytes).
|
||||
*
|
||||
* Note that a newline character ("\n") will be appended automatically to your
|
||||
* log message, if not already there. It is not possible to send several
|
||||
* messages and have them appear on a single line in logcat.
|
||||
*
|
||||
* Please use logging in moderation:
|
||||
*
|
||||
* - Sending log messages eats CPU and slow down your application and the
|
||||
* system.
|
||||
*
|
||||
* - The circular log buffer is pretty small, so sending many messages
|
||||
* will hide other important log messages.
|
||||
*
|
||||
* - In release builds, only send log messages to account for exceptional
|
||||
* conditions.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN)
|
||||
#define __INTRODUCED_IN(x)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Android log priority values, in increasing order of priority.
|
||||
*/
|
||||
typedef enum android_LogPriority {
|
||||
/** For internal use only. */
|
||||
ANDROID_LOG_UNKNOWN = 0,
|
||||
/** The default priority, for internal use only. */
|
||||
ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
|
||||
/** Verbose logging. Should typically be disabled for a release apk. */
|
||||
ANDROID_LOG_VERBOSE,
|
||||
/** Debug logging. Should typically be disabled for a release apk. */
|
||||
ANDROID_LOG_DEBUG,
|
||||
/** Informational logging. Should typically be disabled for a release apk. */
|
||||
ANDROID_LOG_INFO,
|
||||
/** Warning logging. For use with recoverable failures. */
|
||||
ANDROID_LOG_WARN,
|
||||
/** Error logging. For use with unrecoverable failures. */
|
||||
ANDROID_LOG_ERROR,
|
||||
/** Fatal logging. For use when aborting. */
|
||||
ANDROID_LOG_FATAL,
|
||||
/** For internal use only. */
|
||||
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
|
||||
} android_LogPriority;
|
||||
|
||||
/**
|
||||
* Writes the constant string `text` to the log, with priority `prio` and tag
|
||||
* `tag`.
|
||||
*/
|
||||
int __android_log_write(int prio, const char* tag, const char* text);
|
||||
|
||||
/**
|
||||
* Writes a formatted string to the log, with priority `prio` and tag `tag`.
|
||||
* The details of formatting are the same as for
|
||||
* [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
|
||||
*/
|
||||
int __android_log_print(int prio, const char* tag, const char* fmt, ...)
|
||||
__attribute__((__format__(printf, 3, 4)));
|
||||
|
||||
/**
|
||||
* Equivalent to `__android_log_print`, but taking a `va_list`.
|
||||
* (If `__android_log_print` is like `printf`, this is like `vprintf`.)
|
||||
*/
|
||||
int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
|
||||
__attribute__((__format__(printf, 3, 0)));
|
||||
|
||||
/**
|
||||
* Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
|
||||
* stderr, before calling
|
||||
* [abort(3)](http://man7.org/linux/man-pages/man3/abort.3.html).
|
||||
*
|
||||
* If `fmt` is non-null, `cond` is unused. If `fmt` is null, the string
|
||||
* `Assertion failed: %s` is used with `cond` as the string argument.
|
||||
* If both `fmt` and `cond` are null, a default string is provided.
|
||||
*
|
||||
* Most callers should use
|
||||
* [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
|
||||
* `<assert.h>` instead, or the `__assert` and `__assert2` functions
|
||||
* provided by bionic if more control is needed. They support automatically
|
||||
* including the source filename and line number more conveniently than this
|
||||
* function.
|
||||
*/
|
||||
void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...)
|
||||
__attribute__((__noreturn__)) __attribute__((__format__(printf, 3, 4)));
|
||||
|
||||
/**
|
||||
* Identifies a specific log buffer for __android_log_buf_write()
|
||||
* and __android_log_buf_print().
|
||||
*/
|
||||
typedef enum log_id {
|
||||
LOG_ID_MIN = 0,
|
||||
|
||||
/** The main log buffer. This is the only log buffer available to apps. */
|
||||
LOG_ID_MAIN = 0,
|
||||
/** The radio log buffer. */
|
||||
LOG_ID_RADIO = 1,
|
||||
/** The event log buffer. */
|
||||
LOG_ID_EVENTS = 2,
|
||||
/** The system log buffer. */
|
||||
LOG_ID_SYSTEM = 3,
|
||||
/** The crash log buffer. */
|
||||
LOG_ID_CRASH = 4,
|
||||
/** The statistics log buffer. */
|
||||
LOG_ID_STATS = 5,
|
||||
/** The security log buffer. */
|
||||
LOG_ID_SECURITY = 6,
|
||||
/** The kernel log buffer. */
|
||||
LOG_ID_KERNEL = 7,
|
||||
|
||||
LOG_ID_MAX,
|
||||
|
||||
/** Let the logging function choose the best log target. */
|
||||
LOG_ID_DEFAULT = 0x7FFFFFFF
|
||||
} log_id_t;
|
||||
|
||||
/**
|
||||
* Writes the constant string `text` to the log buffer `id`,
|
||||
* with priority `prio` and tag `tag`.
|
||||
*
|
||||
* Apps should use __android_log_write() instead.
|
||||
*/
|
||||
int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
|
||||
|
||||
/**
|
||||
* Writes a formatted string to log buffer `id`,
|
||||
* with priority `prio` and tag `tag`.
|
||||
* The details of formatting are the same as for
|
||||
* [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
|
||||
*
|
||||
* Apps should use __android_log_print() instead.
|
||||
*/
|
||||
int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
|
||||
__attribute__((__format__(printf, 4, 5)));
|
||||
|
||||
/**
|
||||
* Logger data struct used for writing log messages to liblog via __android_log_write_logger_data()
|
||||
* and sending log messages to user defined loggers specified in __android_log_set_logger().
|
||||
*/
|
||||
struct __android_log_message {
|
||||
/** Must be set to sizeof(__android_log_message) and is used for versioning. */
|
||||
size_t struct_size;
|
||||
|
||||
/** {@link log_id_t} values. */
|
||||
int32_t buffer_id;
|
||||
|
||||
/** {@link android_LogPriority} values. */
|
||||
int32_t priority;
|
||||
|
||||
/** The tag for the log message. */
|
||||
const char* tag;
|
||||
|
||||
/** Optional file name, may be set to nullptr. */
|
||||
const char* file;
|
||||
|
||||
/** Optional line number, ignore if file is nullptr. */
|
||||
uint32_t line;
|
||||
|
||||
/** The log message itself. */
|
||||
const char* message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prototype for the 'logger' function that is called for every log message.
|
||||
*/
|
||||
typedef void (*__android_logger_function)(const struct __android_log_message* log_message);
|
||||
/**
|
||||
* Prototype for the 'abort' function that is called when liblog will abort due to
|
||||
* __android_log_assert() failures.
|
||||
*/
|
||||
typedef void (*__android_aborter_function)(const char* abort_message);
|
||||
|
||||
#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
|
||||
/**
|
||||
* Writes the log message specified by log_message. log_message includes additional file name and
|
||||
* line number information that a logger may use. log_message is versioned for backwards
|
||||
* compatibility.
|
||||
* This assumes that loggability has already been checked through __android_log_is_loggable().
|
||||
* Higher level logging libraries, such as libbase, first check loggability, then format their
|
||||
* buffers, then pass the message to liblog via this function, and therefore we do not want to
|
||||
* duplicate the loggability check here.
|
||||
*
|
||||
* @param log_message the log message itself, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_write_log_message(struct __android_log_message* log_message) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets a user defined logger function. All log messages sent to liblog will be set to the
|
||||
* function pointer specified by logger for processing. It is not expected that log messages are
|
||||
* already terminated with a new line. This function should add new lines if required for line
|
||||
* separation.
|
||||
*
|
||||
* @param logger the new function that will handle log messages.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Writes the log message to logd. This is an __android_logger_function and can be provided to
|
||||
* __android_log_set_logger(). It is the default logger when running liblog on a device.
|
||||
*
|
||||
* @param log_message the log message to write, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_logd_logger(const struct __android_log_message* log_message) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Writes the log message to stderr. This is an __android_logger_function and can be provided to
|
||||
* __android_log_set_logger(). It is the default logger when running liblog on host.
|
||||
*
|
||||
* @param log_message the log message to write, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_stderr_logger(const struct __android_log_message* log_message)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets a user defined aborter function that is called for __android_log_assert() failures. This
|
||||
* user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
|
||||
* required to.
|
||||
*
|
||||
* @param aborter the new aborter function, see __android_aborter_function.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_aborter(__android_aborter_function aborter) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Calls the stored aborter function. This allows for other logging libraries to use the same
|
||||
* aborter function by calling this function in liblog.
|
||||
*
|
||||
* @param abort_message an additional message supplied when aborting, for example this is used to
|
||||
* call android_set_abort_message() in __android_log_default_aborter().
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_call_aborter(const char* abort_message) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets android_set_abort_message() on device then aborts(). This is the default aborter.
|
||||
*
|
||||
* @param abort_message an additional message supplied when aborting. This functions calls
|
||||
* android_set_abort_message() with its contents.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_default_aborter(const char* abort_message) __attribute__((noreturn))
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
|
||||
* __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
|
||||
* be printed. A non-zero result indicates yes, zero indicates false.
|
||||
*
|
||||
* If both a priority for a tag and a minimum priority are set by
|
||||
* __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
|
||||
* minimum priority needed to log. If only one is set, then that value is used to determine the
|
||||
* minimum priority needed. If none are set, then default_priority is used.
|
||||
*
|
||||
* @param prio the priority to test, takes android_LogPriority values.
|
||||
* @param tag the tag to test.
|
||||
* @param default_prio the default priority to use if no properties or minimum priority are set.
|
||||
* @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
|
||||
* __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
|
||||
* be printed. A non-zero result indicates yes, zero indicates false.
|
||||
*
|
||||
* If both a priority for a tag and a minimum priority are set by
|
||||
* __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
|
||||
* minimum priority needed to log. If only one is set, then that value is used to determine the
|
||||
* minimum priority needed. If none are set, then default_priority is used.
|
||||
*
|
||||
* @param prio the priority to test, takes android_LogPriority values.
|
||||
* @param tag the tag to test.
|
||||
* @param len the length of the tag.
|
||||
* @param default_prio the default priority to use if no properties or minimum priority are set.
|
||||
* @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets the minimum priority that will be logged for this process.
|
||||
*
|
||||
* @param priority the new minimum priority to set, takes android_LogPriority values.
|
||||
* @return the previous set minimum priority as android_LogPriority values, or
|
||||
* ANDROID_LOG_DEFAULT if none was set.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int32_t __android_log_set_minimum_priority(int32_t priority) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Gets the minimum priority that will be logged for this process. If none has been set by a
|
||||
* previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
|
||||
*
|
||||
* @return the current minimum priority as android_LogPriority values, or
|
||||
* ANDROID_LOG_DEFAULT if none is set.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int32_t __android_log_get_minimum_priority(void) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets the default tag if no tag is provided when writing a log message. Defaults to
|
||||
* getprogname(). This truncates tag to the maximum log message size, though appropriate tags
|
||||
* should be much smaller.
|
||||
*
|
||||
* @param tag the new log tag.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_default_tag(const char* tag) __INTRODUCED_IN(30);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
|
||||
|
||||
struct EventTagMap;
|
||||
typedef struct EventTagMap EventTagMap;
|
||||
|
||||
/*
|
||||
* Open the specified file as an event log tag map.
|
||||
*
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
EventTagMap* android_openEventTagMap(const char* fileName);
|
||||
|
||||
/*
|
||||
* Close the map.
|
||||
*/
|
||||
void android_closeEventTagMap(EventTagMap* map);
|
||||
|
||||
/*
|
||||
* Look up a tag by index. Returns the tag string & string length, or NULL if
|
||||
* not found. Returned string is not guaranteed to be nul terminated.
|
||||
*/
|
||||
const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len,
|
||||
unsigned int tag);
|
||||
|
||||
/*
|
||||
* Look up a format by index. Returns the format string & string length,
|
||||
* or NULL if not found. Returned string is not guaranteed to be nul terminated.
|
||||
*/
|
||||
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len,
|
||||
unsigned int tag);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Too many in the ecosystem assume these are included */
|
||||
#if !defined(_WIN32)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <stdint.h> /* uint16_t, int32_t */
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <log/log_id.h>
|
||||
#include <log/log_main.h>
|
||||
#include <log/log_radio.h>
|
||||
#include <log/log_safetynet.h>
|
||||
#include <log/log_system.h>
|
||||
#include <log/log_time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* LOG_TAG is the local tag used for the following simplified
|
||||
* logging macros. You can change this preprocessor definition
|
||||
* before using the other macros to change the tag.
|
||||
*/
|
||||
|
||||
#ifndef LOG_TAG
|
||||
#define LOG_TAG NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Normally we strip the effects of ALOGV (VERBOSE messages),
|
||||
* LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
|
||||
* release builds be defining NDEBUG. You can modify this (for
|
||||
* example with "#define LOG_NDEBUG 0" at the top of your source
|
||||
* file) to change that behavior.
|
||||
*/
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The maximum size of the log entry payload that can be
|
||||
* written to the logger. An attempt to write more than
|
||||
* this amount will result in a truncated log entry.
|
||||
*/
|
||||
#define LOGGER_ENTRY_MAX_PAYLOAD 4068
|
||||
|
||||
/*
|
||||
* Event logging.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following should not be used directly.
|
||||
*/
|
||||
|
||||
int __android_log_bwrite(int32_t tag, const void* payload, size_t len);
|
||||
int __android_log_btwrite(int32_t tag, char type, const void* payload,
|
||||
size_t len);
|
||||
int __android_log_bswrite(int32_t tag, const char* payload);
|
||||
|
||||
int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
|
||||
|
||||
#define android_bWriteLog(tag, payload, len) \
|
||||
__android_log_bwrite(tag, payload, len)
|
||||
#define android_btWriteLog(tag, type, payload, len) \
|
||||
__android_log_btwrite(tag, type, payload, len)
|
||||
|
||||
/*
|
||||
* Event log entry types.
|
||||
*/
|
||||
typedef enum {
|
||||
/* Special markers for android_log_list_element type */
|
||||
EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */
|
||||
EVENT_TYPE_UNKNOWN = '?', /* protocol error */
|
||||
|
||||
/* must match with declaration in java/android/android/util/EventLog.java */
|
||||
EVENT_TYPE_INT = 0, /* int32_t */
|
||||
EVENT_TYPE_LONG = 1, /* int64_t */
|
||||
EVENT_TYPE_STRING = 2,
|
||||
EVENT_TYPE_LIST = 3,
|
||||
EVENT_TYPE_FLOAT = 4,
|
||||
} AndroidEventLogType;
|
||||
|
||||
#ifndef LOG_EVENT_INT
|
||||
#define LOG_EVENT_INT(_tag, _value) \
|
||||
{ \
|
||||
int intBuf = _value; \
|
||||
(void)android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, sizeof(intBuf)); \
|
||||
}
|
||||
#endif
|
||||
#ifndef LOG_EVENT_LONG
|
||||
#define LOG_EVENT_LONG(_tag, _value) \
|
||||
{ \
|
||||
long long longBuf = _value; \
|
||||
(void)android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)); \
|
||||
}
|
||||
#endif
|
||||
#ifndef LOG_EVENT_FLOAT
|
||||
#define LOG_EVENT_FLOAT(_tag, _value) \
|
||||
{ \
|
||||
float floatBuf = _value; \
|
||||
(void)android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
|
||||
sizeof(floatBuf)); \
|
||||
}
|
||||
#endif
|
||||
#ifndef LOG_EVENT_STRING
|
||||
#define LOG_EVENT_STRING(_tag, _value) \
|
||||
(void)__android_log_bswrite(_tag, _value);
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Release any logger resources (a new log write will immediately re-acquire)
|
||||
*
|
||||
* This is specifically meant to be used by Zygote to close open file descriptors after fork()
|
||||
* and before specialization. O_CLOEXEC is used on file descriptors, so they will be closed upon
|
||||
* exec() in normal use cases.
|
||||
*
|
||||
* Note that this is not safe to call from a multi-threaded program.
|
||||
*/
|
||||
void __android_log_close(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,278 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* For manipulating lists of events. */
|
||||
|
||||
#define ANDROID_MAX_LIST_NEST_DEPTH 8
|
||||
|
||||
/*
|
||||
* The opaque context used to manipulate lists of events.
|
||||
*/
|
||||
typedef struct android_log_context_internal* android_log_context;
|
||||
|
||||
/*
|
||||
* Elements returned when reading a list of events.
|
||||
*/
|
||||
typedef struct {
|
||||
AndroidEventLogType type;
|
||||
uint16_t complete;
|
||||
uint16_t len;
|
||||
union {
|
||||
int32_t int32;
|
||||
int64_t int64;
|
||||
char* string;
|
||||
float float32;
|
||||
} data;
|
||||
} android_log_list_element;
|
||||
|
||||
/*
|
||||
* Creates a context associated with an event tag to write elements to
|
||||
* the list of events.
|
||||
*/
|
||||
android_log_context create_android_logger(uint32_t tag);
|
||||
|
||||
/* All lists must be braced by a begin and end call */
|
||||
/*
|
||||
* NB: If the first level braces are missing when specifying multiple
|
||||
* elements, we will manufacturer a list to embrace it for your API
|
||||
* convenience. For a single element, it will remain solitary.
|
||||
*/
|
||||
int android_log_write_list_begin(android_log_context ctx);
|
||||
int android_log_write_list_end(android_log_context ctx);
|
||||
|
||||
int android_log_write_int32(android_log_context ctx, int32_t value);
|
||||
int android_log_write_int64(android_log_context ctx, int64_t value);
|
||||
int android_log_write_string8(android_log_context ctx, const char* value);
|
||||
int android_log_write_string8_len(android_log_context ctx, const char* value,
|
||||
size_t maxlen);
|
||||
int android_log_write_float32(android_log_context ctx, float value);
|
||||
|
||||
/* Submit the composed list context to the specified logger id */
|
||||
/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
|
||||
int android_log_write_list(android_log_context ctx, log_id_t id);
|
||||
|
||||
/*
|
||||
* Creates a context from a raw buffer representing a list of events to be read.
|
||||
*/
|
||||
android_log_context create_android_log_parser(const char* msg, size_t len);
|
||||
|
||||
android_log_list_element android_log_read_next(android_log_context ctx);
|
||||
android_log_list_element android_log_peek_next(android_log_context ctx);
|
||||
|
||||
/* Reset writer context */
|
||||
int android_log_reset(android_log_context ctx);
|
||||
|
||||
/* Reset reader context */
|
||||
int android_log_parser_reset(android_log_context ctx,
|
||||
const char* msg, size_t len);
|
||||
|
||||
/* Finished with reader or writer context */
|
||||
int android_log_destroy(android_log_context* ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* android_log_list C++ helpers */
|
||||
extern "C++" {
|
||||
class android_log_event_list {
|
||||
private:
|
||||
android_log_context ctx;
|
||||
int ret;
|
||||
|
||||
android_log_event_list(const android_log_event_list&) = delete;
|
||||
void operator=(const android_log_event_list&) = delete;
|
||||
|
||||
public:
|
||||
explicit android_log_event_list(int tag) : ret(0) {
|
||||
ctx = create_android_logger(static_cast<uint32_t>(tag));
|
||||
}
|
||||
~android_log_event_list() {
|
||||
android_log_destroy(&ctx);
|
||||
}
|
||||
|
||||
int close() {
|
||||
int retval = android_log_destroy(&ctx);
|
||||
if (retval < 0) ret = retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* To allow above C calls to use this class as parameter */
|
||||
operator android_log_context() const {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/* return errors or transmit status */
|
||||
int status() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int begin() {
|
||||
int retval = android_log_write_list_begin(ctx);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
int end() {
|
||||
int retval = android_log_write_list_end(ctx);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(int32_t value) {
|
||||
int retval = android_log_write_int32(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(uint32_t value) {
|
||||
int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(bool value) {
|
||||
int retval = android_log_write_int32(ctx, value ? 1 : 0);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(int64_t value) {
|
||||
int retval = android_log_write_int64(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(uint64_t value) {
|
||||
int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(const char* value) {
|
||||
int retval = android_log_write_string8(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(const std::string& value) {
|
||||
int retval =
|
||||
android_log_write_string8_len(ctx, value.data(), value.length());
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(float value) {
|
||||
int retval = android_log_write_float32(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int write(log_id_t id = LOG_ID_EVENTS) {
|
||||
/* facilitate -EBUSY retry */
|
||||
if ((ret == -EBUSY) || (ret > 0)) ret = 0;
|
||||
int retval = android_log_write_list(ctx, id);
|
||||
/* existing errors trump transmission errors */
|
||||
if (!ret) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int operator<<(log_id_t id) {
|
||||
write(id);
|
||||
android_log_destroy(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Append<Type> methods removes any integer promotion
|
||||
* confusion, and adds access to string with length.
|
||||
* Append methods are also added for all types for
|
||||
* convenience.
|
||||
*/
|
||||
|
||||
bool AppendInt(int32_t value) {
|
||||
int retval = android_log_write_int32(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool AppendLong(int64_t value) {
|
||||
int retval = android_log_write_int64(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool AppendString(const char* value) {
|
||||
int retval = android_log_write_string8(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool AppendString(const char* value, size_t len) {
|
||||
int retval = android_log_write_string8_len(ctx, value, len);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool AppendString(const std::string& value) {
|
||||
int retval =
|
||||
android_log_write_string8_len(ctx, value.data(), value.length());
|
||||
if (retval < 0) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Append(const std::string& value) {
|
||||
int retval =
|
||||
android_log_write_string8_len(ctx, value.data(), value.length());
|
||||
if (retval < 0) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AppendFloat(float value) {
|
||||
int retval = android_log_write_float32(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
template <typename Tvalue>
|
||||
bool Append(Tvalue value) {
|
||||
*this << value;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool Append(const char* value, size_t len) {
|
||||
int retval = android_log_write_string8_len(ctx, value, len);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* log_id_t helpers
|
||||
*/
|
||||
log_id_t android_name_to_log_id(const char* logName);
|
||||
const char* android_log_id_to_name(log_id_t log_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,380 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* Normally we strip the effects of ALOGV (VERBOSE messages),
|
||||
* LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
|
||||
* release builds be defining NDEBUG. You can modify this (for
|
||||
* example with "#define LOG_NDEBUG 0" at the top of your source
|
||||
* file) to change that behavior.
|
||||
*/
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* This file uses ", ## __VA_ARGS__" zero-argument token pasting to
|
||||
* work around issues with debug-only syntax errors in assertions
|
||||
* that are missing format strings. See commit
|
||||
* 19299904343daf191267564fe32e6cd5c165cd42
|
||||
*/
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use __VA_ARGS__ if running a static analyzer,
|
||||
* to avoid warnings of unused variables in __VA_ARGS__.
|
||||
* Use constexpr function in C++ mode, so these macros can be used
|
||||
* in other constexpr functions without warning.
|
||||
*/
|
||||
#ifdef __clang_analyzer__
|
||||
#ifdef __cplusplus
|
||||
extern "C++" {
|
||||
template <typename... Ts>
|
||||
constexpr int __fake_use_va_args(Ts...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
extern int __fake_use_va_args(int, ...);
|
||||
#endif /* __cplusplus */
|
||||
#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
|
||||
#else
|
||||
#define __FAKE_USE_VA_ARGS(...) ((void)(0))
|
||||
#endif /* __clang_analyzer__ */
|
||||
|
||||
#ifndef __predict_false
|
||||
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
|
||||
#endif
|
||||
|
||||
#define android_writeLog(prio, tag, text) __android_log_write(prio, tag, text)
|
||||
|
||||
#define android_printLog(prio, tag, ...) \
|
||||
__android_log_print(prio, tag, __VA_ARGS__)
|
||||
|
||||
#define android_vprintLog(prio, cond, tag, ...) \
|
||||
__android_log_vprint(prio, tag, __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Log macro that allows you to specify a number for the priority.
|
||||
*/
|
||||
#ifndef LOG_PRI
|
||||
#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log macro that allows you to pass in a varargs ("args" is a va_list).
|
||||
*/
|
||||
#ifndef LOG_PRI_VA
|
||||
#define LOG_PRI_VA(priority, tag, fmt, args) \
|
||||
android_vprintLog(priority, NULL, tag, fmt, args)
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* XXX Macros to work around syntax errors in places where format string
|
||||
* arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
|
||||
* (happens only in debug builds).
|
||||
*/
|
||||
|
||||
/* Returns 2nd arg. Used to substitute default value if caller's vararg list
|
||||
* is empty.
|
||||
*/
|
||||
#define __android_second(dummy, second, ...) second
|
||||
|
||||
/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
|
||||
* returns nothing.
|
||||
*/
|
||||
#define __android_rest(first, ...) , ##__VA_ARGS__
|
||||
|
||||
#define android_printAssert(cond, tag, ...) \
|
||||
__android_log_assert(cond, tag, \
|
||||
__android_second(0, ##__VA_ARGS__, NULL) \
|
||||
__android_rest(__VA_ARGS__))
|
||||
|
||||
/*
|
||||
* Log a fatal error. If the given condition fails, this stops program
|
||||
* execution like a normal assertion, but also generating the given message.
|
||||
* It is NOT stripped from release builds. Note that the condition test
|
||||
* is -inverted- from the normal assert() semantics.
|
||||
*/
|
||||
#ifndef LOG_ALWAYS_FATAL_IF
|
||||
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), \
|
||||
((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
#ifndef LOG_ALWAYS_FATAL
|
||||
#define LOG_ALWAYS_FATAL(...) \
|
||||
(((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
|
||||
* are stripped out of release builds.
|
||||
*/
|
||||
|
||||
#if LOG_NDEBUG
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef LOG_FATAL
|
||||
#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef LOG_FATAL
|
||||
#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assertion that generates a log message when the assertion fails.
|
||||
* Stripped out of release builds. Uses the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOG_ASSERT
|
||||
#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* C/C++ logging functions. See the logging documentation for API details.
|
||||
*
|
||||
* We'd like these to be available from C code (in case we import some from
|
||||
* somewhere), so this has a C interface.
|
||||
*
|
||||
* The output will be correct when the log file is shared between multiple
|
||||
* threads and/or multiple processes so long as the operating system
|
||||
* supports O_APPEND. These calls have mutex-protected data structures
|
||||
* and so are NOT reentrant. Do not use LOG in a signal handler.
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGV
|
||||
#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#if LOG_NDEBUG
|
||||
#define ALOGV(...) \
|
||||
do { \
|
||||
__FAKE_USE_VA_ARGS(__VA_ARGS__); \
|
||||
if (false) { \
|
||||
__ALOGV(__VA_ARGS__); \
|
||||
} \
|
||||
} while (false)
|
||||
#else
|
||||
#define ALOGV(...) __ALOGV(__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ALOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
|
||||
#else
|
||||
#define ALOGV_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGD
|
||||
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGD_IF
|
||||
#define ALOGD_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGI
|
||||
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGI_IF
|
||||
#define ALOGI_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGW
|
||||
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGW_IF
|
||||
#define ALOGW_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGE
|
||||
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGE_IF
|
||||
#define ALOGE_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* verbose priority.
|
||||
*/
|
||||
#ifndef IF_ALOGV
|
||||
#if LOG_NDEBUG
|
||||
#define IF_ALOGV() if (false)
|
||||
#else
|
||||
#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* debug priority.
|
||||
*/
|
||||
#ifndef IF_ALOGD
|
||||
#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* info priority.
|
||||
*/
|
||||
#ifndef IF_ALOGI
|
||||
#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* warn priority.
|
||||
*/
|
||||
#ifndef IF_ALOGW
|
||||
#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* error priority.
|
||||
*/
|
||||
#ifndef IF_ALOGE
|
||||
#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Basic log message macro.
|
||||
*
|
||||
* Example:
|
||||
* ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
|
||||
*
|
||||
* The second argument may be NULL or "" to indicate the "global" tag.
|
||||
*/
|
||||
#ifndef ALOG
|
||||
#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional given a desired logging priority and tag.
|
||||
*/
|
||||
#ifndef IF_ALOG
|
||||
#define IF_ALOG(priority, tag) if (android_testLog(ANDROID_##priority, tag))
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
|
||||
* android_testLog will remain constant in its purpose as a wrapper
|
||||
* for Android logging filter policy, and can be subject to
|
||||
* change. It can be reused by the developers that override
|
||||
* IF_ALOG as a convenient means to reimplement their policy
|
||||
* over Android.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Use the per-tag properties "log.tag.<tagname>" to generate a runtime
|
||||
* result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
|
||||
* ANDROID_LOG_FATAL. default_prio if no property. Undefined behavior if
|
||||
* any other value.
|
||||
*/
|
||||
int __android_log_is_loggable(int prio, const char* tag, int default_prio);
|
||||
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
|
||||
|
||||
#if LOG_NDEBUG /* Production */
|
||||
#define android_testLog(prio, tag) \
|
||||
(__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
|
||||
ANDROID_LOG_DEBUG) != 0)
|
||||
#else
|
||||
#define android_testLog(prio, tag) \
|
||||
(__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
|
||||
ANDROID_LOG_VERBOSE) != 0)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Returns `1` if the device is debuggable or `0` if not. */
|
||||
int __android_log_is_debuggable();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
/*
|
||||
* Normally we strip the effects of ALOGV (VERBOSE messages),
|
||||
* LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
|
||||
* release builds be defining NDEBUG. You can modify this (for
|
||||
* example with "#define LOG_NDEBUG 0" at the top of your source
|
||||
* file) to change that behavior.
|
||||
*/
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#ifndef __predict_false
|
||||
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose radio log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGV
|
||||
#define __RLOGV(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGV(...) \
|
||||
do { \
|
||||
if (0) { \
|
||||
__RLOGV(__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define RLOGV(...) __RLOGV(__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef RLOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define RLOGV_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug radio log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGD
|
||||
#define RLOGD(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGD_IF
|
||||
#define RLOGD_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info radio log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGI
|
||||
#define RLOGI(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGI_IF
|
||||
#define RLOGI_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning radio log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGW
|
||||
#define RLOGW(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGW_IF
|
||||
#define RLOGW_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error radio log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGE
|
||||
#define RLOGE(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGE_IF
|
||||
#define RLOGE_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <log/log_time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
|
||||
|
||||
/*
|
||||
* Native log reading interface section. See logcat for sample code.
|
||||
*
|
||||
* The preferred API is an exec of logcat. Likely uses of this interface
|
||||
* are if native code suffers from exec or filtration being too costly,
|
||||
* access to raw information, or parsing is an issue.
|
||||
*/
|
||||
|
||||
struct logger_entry {
|
||||
uint16_t len; /* length of the payload */
|
||||
uint16_t hdr_size; /* sizeof(struct logger_entry) */
|
||||
int32_t pid; /* generating process's pid */
|
||||
uint32_t tid; /* generating process's tid */
|
||||
uint32_t sec; /* seconds since Epoch */
|
||||
uint32_t nsec; /* nanoseconds */
|
||||
uint32_t lid; /* log id of the payload, bottom 4 bits currently */
|
||||
uint32_t uid; /* generating process's uid */
|
||||
};
|
||||
|
||||
/*
|
||||
* The maximum size of a log entry which can be read.
|
||||
* An attempt to read less than this amount may result
|
||||
* in read() returning EINVAL.
|
||||
*/
|
||||
#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
|
||||
|
||||
struct log_msg {
|
||||
union {
|
||||
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
|
||||
struct logger_entry entry;
|
||||
} __attribute__((aligned(4)));
|
||||
#ifdef __cplusplus
|
||||
uint64_t nsec() const {
|
||||
return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
|
||||
}
|
||||
log_id_t id() {
|
||||
return static_cast<log_id_t>(entry.lid);
|
||||
}
|
||||
char* msg() {
|
||||
unsigned short hdr_size = entry.hdr_size;
|
||||
if (hdr_size >= sizeof(struct log_msg) - sizeof(entry)) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<char*>(buf) + hdr_size;
|
||||
}
|
||||
unsigned int len() { return entry.hdr_size + entry.len; }
|
||||
#endif
|
||||
};
|
||||
|
||||
struct logger;
|
||||
|
||||
log_id_t android_logger_get_id(struct logger* logger);
|
||||
|
||||
/* Clears the given log buffer. */
|
||||
int android_logger_clear(struct logger* logger);
|
||||
/* Return the allotted size for the given log buffer. */
|
||||
long android_logger_get_log_size(struct logger* logger);
|
||||
/* Set the allotted size for the given log buffer. */
|
||||
int android_logger_set_log_size(struct logger* logger, unsigned long size);
|
||||
/* Return the actual, uncompressed size that can be read from the given log buffer. */
|
||||
long android_logger_get_log_readable_size(struct logger* logger);
|
||||
/* Return the actual, compressed size that the given log buffer is consuming. */
|
||||
long android_logger_get_log_consumed_size(struct logger* logger);
|
||||
/* Deprecated. Always returns '4' regardless of input. */
|
||||
int android_logger_get_log_version(struct logger* logger);
|
||||
|
||||
struct logger_list;
|
||||
|
||||
ssize_t android_logger_get_statistics(struct logger_list* logger_list,
|
||||
char* buf, size_t len);
|
||||
ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
|
||||
char* buf, size_t len);
|
||||
int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
|
||||
|
||||
/* The below values are used for the `mode` argument of the below functions. */
|
||||
/* Note that 0x00000003 were previously used and should be considered reserved. */
|
||||
#define ANDROID_LOG_NONBLOCK 0x00000800
|
||||
#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
|
||||
#define ANDROID_LOG_PSTORE 0x80000000
|
||||
|
||||
struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
|
||||
pid_t pid);
|
||||
struct logger_list* android_logger_list_alloc_time(int mode, log_time start,
|
||||
pid_t pid);
|
||||
void android_logger_list_free(struct logger_list* logger_list);
|
||||
/* In the purest sense, the following two are orthogonal interfaces */
|
||||
int android_logger_list_read(struct logger_list* logger_list,
|
||||
struct log_msg* log_msg);
|
||||
|
||||
/* Multiple log_id_t opens */
|
||||
struct logger* android_logger_open(struct logger_list* logger_list, log_id_t id);
|
||||
/* Single log_id_t open */
|
||||
struct logger_list* android_logger_list_open(log_id_t id, int mode,
|
||||
unsigned int tail, pid_t pid);
|
||||
#define android_logger_list_close android_logger_list_free
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define android_errorWriteLog(tag, subTag) \
|
||||
__android_log_error_write(tag, subTag, -1, NULL, 0)
|
||||
|
||||
#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
|
||||
__android_log_error_write(tag, subTag, uid, data, dataLen)
|
||||
|
||||
int __android_log_error_write(int tag, const char* subTag, int32_t uid,
|
||||
const char* data, uint32_t dataLen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
/*
|
||||
* Normally we strip the effects of ALOGV (VERBOSE messages),
|
||||
* LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
|
||||
* release builds be defining NDEBUG. You can modify this (for
|
||||
* example with "#define LOG_NDEBUG 0" at the top of your source
|
||||
* file) to change that behavior.
|
||||
*/
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __predict_false
|
||||
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGV
|
||||
#define __SLOGV(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#if LOG_NDEBUG
|
||||
#define SLOGV(...) \
|
||||
do { \
|
||||
if (0) { \
|
||||
__SLOGV(__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define SLOGV(...) __SLOGV(__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef SLOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define SLOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define SLOGV_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGD
|
||||
#define SLOGD(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGD_IF
|
||||
#define SLOGD_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGI
|
||||
#define SLOGI(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGI_IF
|
||||
#define SLOGI_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGW
|
||||
#define SLOGW(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGW_IF
|
||||
#define SLOGW_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGE
|
||||
#define SLOGE(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGE_IF
|
||||
#define SLOGE_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
/* struct log_time is a wire-format variant of struct timespec */
|
||||
#define NS_PER_SEC 1000000000ULL
|
||||
#define US_PER_SEC 1000000ULL
|
||||
#define MS_PER_SEC 1000ULL
|
||||
|
||||
#define LOG_TIME_SEC(t) ((t)->tv_sec)
|
||||
/* next power of two after NS_PER_SEC */
|
||||
#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct log_time {
|
||||
public:
|
||||
uint32_t tv_sec = 0; /* good to Feb 5 2106 */
|
||||
uint32_t tv_nsec = 0;
|
||||
|
||||
static constexpr timespec EPOCH = {0, 0};
|
||||
|
||||
log_time() {}
|
||||
explicit log_time(const timespec& T)
|
||||
: tv_sec(static_cast<uint32_t>(T.tv_sec)), tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {}
|
||||
explicit log_time(uint32_t sec, uint32_t nsec = 0)
|
||||
: tv_sec(sec), tv_nsec(nsec) {
|
||||
}
|
||||
#ifdef __linux__
|
||||
explicit log_time(clockid_t id) {
|
||||
timespec T;
|
||||
clock_gettime(id, &T);
|
||||
tv_sec = static_cast<uint32_t>(T.tv_sec);
|
||||
tv_nsec = static_cast<uint32_t>(T.tv_nsec);
|
||||
}
|
||||
#endif
|
||||
/* timespec */
|
||||
bool operator==(const timespec& T) const {
|
||||
return (tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
|
||||
(tv_nsec == static_cast<uint32_t>(T.tv_nsec));
|
||||
}
|
||||
bool operator!=(const timespec& T) const {
|
||||
return !(*this == T);
|
||||
}
|
||||
bool operator<(const timespec& T) const {
|
||||
return (tv_sec < static_cast<uint32_t>(T.tv_sec)) ||
|
||||
((tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
|
||||
(tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
|
||||
}
|
||||
bool operator>=(const timespec& T) const {
|
||||
return !(*this < T);
|
||||
}
|
||||
bool operator>(const timespec& T) const {
|
||||
return (tv_sec > static_cast<uint32_t>(T.tv_sec)) ||
|
||||
((tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
|
||||
(tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
|
||||
}
|
||||
bool operator<=(const timespec& T) const {
|
||||
return !(*this > T);
|
||||
}
|
||||
|
||||
/* log_time */
|
||||
bool operator==(const log_time& T) const {
|
||||
return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
|
||||
}
|
||||
bool operator!=(const log_time& T) const {
|
||||
return !(*this == T);
|
||||
}
|
||||
bool operator<(const log_time& T) const {
|
||||
return (tv_sec < T.tv_sec) ||
|
||||
((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
|
||||
}
|
||||
bool operator>=(const log_time& T) const {
|
||||
return !(*this < T);
|
||||
}
|
||||
bool operator>(const log_time& T) const {
|
||||
return (tv_sec > T.tv_sec) ||
|
||||
((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
|
||||
}
|
||||
bool operator<=(const log_time& T) const {
|
||||
return !(*this > T);
|
||||
}
|
||||
|
||||
log_time operator-=(const log_time& T) {
|
||||
// No concept of negative time, clamp to EPOCH
|
||||
if (*this <= T) {
|
||||
return *this = log_time(EPOCH);
|
||||
}
|
||||
|
||||
if (this->tv_nsec < T.tv_nsec) {
|
||||
--this->tv_sec;
|
||||
this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
|
||||
} else {
|
||||
this->tv_nsec -= T.tv_nsec;
|
||||
}
|
||||
this->tv_sec -= T.tv_sec;
|
||||
|
||||
return *this;
|
||||
}
|
||||
log_time operator-(const log_time& T) const {
|
||||
log_time local(*this);
|
||||
return local -= T;
|
||||
}
|
||||
log_time operator+=(const log_time& T) {
|
||||
this->tv_nsec += T.tv_nsec;
|
||||
if (this->tv_nsec >= NS_PER_SEC) {
|
||||
this->tv_nsec -= NS_PER_SEC;
|
||||
++this->tv_sec;
|
||||
}
|
||||
this->tv_sec += T.tv_sec;
|
||||
|
||||
return *this;
|
||||
}
|
||||
log_time operator+(const log_time& T) const {
|
||||
log_time local(*this);
|
||||
return local += T;
|
||||
}
|
||||
|
||||
uint64_t nsec() const {
|
||||
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
|
||||
}
|
||||
uint64_t usec() const {
|
||||
return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
|
||||
tv_nsec / (NS_PER_SEC / US_PER_SEC);
|
||||
}
|
||||
uint64_t msec() const {
|
||||
return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
|
||||
tv_nsec / (NS_PER_SEC / MS_PER_SEC);
|
||||
}
|
||||
|
||||
/* Add %#q for the fraction of a second to the standard library functions */
|
||||
char* strptime(const char* s, const char* format);
|
||||
} __attribute__((__packed__));
|
||||
}
|
||||
|
||||
#else /* __cplusplus */
|
||||
|
||||
typedef struct log_time {
|
||||
uint32_t tv_sec;
|
||||
uint32_t tv_nsec;
|
||||
} __attribute__((__packed__)) log_time;
|
||||
|
||||
#endif /* __cplusplus */
|
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <log/event_tag_map.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
/* Verbs */
|
||||
FORMAT_OFF = 0,
|
||||
FORMAT_BRIEF,
|
||||
FORMAT_PROCESS,
|
||||
FORMAT_TAG,
|
||||
FORMAT_THREAD,
|
||||
FORMAT_RAW,
|
||||
FORMAT_TIME,
|
||||
FORMAT_THREADTIME,
|
||||
FORMAT_LONG,
|
||||
/* Adverbs. The following are modifiers to above format verbs */
|
||||
FORMAT_MODIFIER_COLOR, /* converts priority to color */
|
||||
FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
|
||||
FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
|
||||
FORMAT_MODIFIER_YEAR, /* Adds year to date */
|
||||
FORMAT_MODIFIER_ZONE, /* Adds zone to date, + UTC */
|
||||
FORMAT_MODIFIER_EPOCH, /* Print time as seconds since Jan 1 1970 */
|
||||
FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
|
||||
FORMAT_MODIFIER_UID, /* Adds uid */
|
||||
FORMAT_MODIFIER_DESCRIPT, /* Adds descriptive */
|
||||
/* private, undocumented */
|
||||
FORMAT_MODIFIER_TIME_NSEC, /* switches from msec to nsec time precision */
|
||||
} AndroidLogPrintFormat;
|
||||
|
||||
typedef struct AndroidLogFormat_t AndroidLogFormat;
|
||||
|
||||
typedef struct AndroidLogEntry_t {
|
||||
time_t tv_sec;
|
||||
long tv_nsec;
|
||||
android_LogPriority priority;
|
||||
int32_t uid;
|
||||
int32_t pid;
|
||||
int32_t tid;
|
||||
const char* tag;
|
||||
size_t tagLen;
|
||||
size_t messageLen;
|
||||
const char* message;
|
||||
} AndroidLogEntry;
|
||||
|
||||
AndroidLogFormat* android_log_format_new();
|
||||
|
||||
void android_log_format_free(AndroidLogFormat* p_format);
|
||||
|
||||
/* currently returns 0 if format is a modifier, 1 if not */
|
||||
int android_log_setPrintFormat(AndroidLogFormat* p_format,
|
||||
AndroidLogPrintFormat format);
|
||||
|
||||
/**
|
||||
* Returns FORMAT_OFF on invalid string
|
||||
*/
|
||||
AndroidLogPrintFormat android_log_formatFromString(const char* s);
|
||||
|
||||
/**
|
||||
* filterExpression: a single filter expression
|
||||
* eg "AT:d"
|
||||
*
|
||||
* returns 0 on success and -1 on invalid expression
|
||||
*
|
||||
* Assumes single threaded execution
|
||||
*
|
||||
*/
|
||||
|
||||
int android_log_addFilterRule(AndroidLogFormat* p_format,
|
||||
const char* filterExpression);
|
||||
|
||||
/**
|
||||
* filterString: a whitespace-separated set of filter expressions
|
||||
* eg "AT:d *:i"
|
||||
*
|
||||
* returns 0 on success and -1 on invalid expression
|
||||
*
|
||||
* Assumes single threaded execution
|
||||
*
|
||||
*/
|
||||
|
||||
int android_log_addFilterString(AndroidLogFormat* p_format,
|
||||
const char* filterString);
|
||||
|
||||
/**
|
||||
* returns 1 if this log line should be printed based on its priority
|
||||
* and tag, and 0 if it should not
|
||||
*/
|
||||
int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
|
||||
android_LogPriority pri);
|
||||
|
||||
/**
|
||||
* Splits a wire-format buffer into an AndroidLogEntry
|
||||
* entry allocated by caller. Pointers will point directly into buf
|
||||
*
|
||||
* Returns 0 on success and -1 on invalid wire format (entry will be
|
||||
* in unspecified state)
|
||||
*/
|
||||
int android_log_processLogBuffer(struct logger_entry* buf,
|
||||
AndroidLogEntry* entry);
|
||||
|
||||
/**
|
||||
* Like android_log_processLogBuffer, but for binary logs.
|
||||
*
|
||||
* If "map" is non-NULL, it will be used to convert the log tag number
|
||||
* into a string.
|
||||
*/
|
||||
int android_log_processBinaryLogBuffer(struct logger_entry* buf,
|
||||
AndroidLogEntry* entry,
|
||||
const EventTagMap* map, char* messageBuf,
|
||||
int messageBufLen);
|
||||
|
||||
/**
|
||||
* Formats a log message into a buffer
|
||||
*
|
||||
* Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
|
||||
* If return value != defaultBuffer, caller must call free()
|
||||
* Returns NULL on malloc error
|
||||
*/
|
||||
|
||||
char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
|
||||
size_t defaultBufferSize,
|
||||
const AndroidLogEntry* p_line,
|
||||
size_t* p_outLength);
|
||||
|
||||
/**
|
||||
* Either print or do not print log line, based on filter
|
||||
*
|
||||
* Assumes single threaded execution
|
||||
*
|
||||
*/
|
||||
int android_log_printLogLine(AndroidLogFormat* p_format, int fd,
|
||||
const AndroidLogEntry* entry);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* This file is used to define the internal protocol for the Android Logger */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Android private interfaces */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <log/log.h>
|
||||
#include <log/log_event_list.h>
|
||||
|
||||
#define LOGGER_MAGIC 'l'
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Header Structure to pstore */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t magic;
|
||||
uint16_t len;
|
||||
uint16_t uid;
|
||||
uint16_t pid;
|
||||
} android_pmsg_log_header_t;
|
||||
|
||||
/* Header Structure to logd, and second header for pstore */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t id;
|
||||
uint16_t tid;
|
||||
log_time realtime;
|
||||
} android_log_header_t;
|
||||
|
||||
/* Event Header Structure to logd */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int32_t tag; // Little Endian Order
|
||||
} android_event_header_t;
|
||||
|
||||
// Event payload EVENT_TYPE_LIST
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_LIST
|
||||
int8_t element_count;
|
||||
} android_event_list_t;
|
||||
|
||||
// Event payload EVENT_TYPE_FLOAT
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_FLOAT
|
||||
float data;
|
||||
} android_event_float_t;
|
||||
|
||||
/* Event payload EVENT_TYPE_INT */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_INT
|
||||
int32_t data; // Little Endian Order
|
||||
} android_event_int_t;
|
||||
|
||||
/* Event with single EVENT_TYPE_INT */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
android_event_header_t header;
|
||||
android_event_int_t payload;
|
||||
} android_log_event_int_t;
|
||||
|
||||
/* Event payload EVENT_TYPE_LONG */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_LONG
|
||||
int64_t data; // Little Endian Order
|
||||
} android_event_long_t;
|
||||
|
||||
/* Event with single EVENT_TYPE_LONG */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
android_event_header_t header;
|
||||
android_event_long_t payload;
|
||||
} android_log_event_long_t;
|
||||
|
||||
/*
|
||||
* Event payload EVENT_TYPE_STRING
|
||||
*
|
||||
* Danger: do not embed this structure into another structure.
|
||||
* This structure uses a flexible array member, and when
|
||||
* compiled using g++, __builtin_object_size(data, 1) returns
|
||||
* a bad value. This is possibly a g++ bug, or a bug due to
|
||||
* the fact that flexible array members are not supported
|
||||
* in C++.
|
||||
* http://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c
|
||||
*/
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_STRING;
|
||||
int32_t length; // Little Endian Order
|
||||
char data[];
|
||||
} android_event_string_t;
|
||||
|
||||
/* Event with single EVENT_TYPE_STRING */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
android_event_header_t header;
|
||||
int8_t type; // EVENT_TYPE_STRING;
|
||||
int32_t length; // Little Endian Order
|
||||
char data[];
|
||||
} android_log_event_string_t;
|
||||
|
||||
#define ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE 256 /* 1MB file */
|
||||
#define ANDROID_LOG_PMSG_FILE_SEQUENCE 1000
|
||||
|
||||
ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio,
|
||||
const char* filename, const char* buf,
|
||||
size_t len);
|
||||
|
||||
#define LOG_ID_ANY ((log_id_t)-1)
|
||||
#define ANDROID_LOG_ANY ANDROID_LOG_UNKNOWN
|
||||
|
||||
/* first 5 arguments match __android_log_msg_file_write, a cast is safe */
|
||||
typedef ssize_t (*__android_log_pmsg_file_read_fn)(log_id_t logId, char prio,
|
||||
const char* filename,
|
||||
const char* buf, size_t len,
|
||||
void* arg);
|
||||
|
||||
ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio,
|
||||
const char* prefix,
|
||||
__android_log_pmsg_file_read_fn fn,
|
||||
void* arg);
|
||||
|
||||
int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len);
|
||||
int __android_log_security_bswrite(int32_t tag, const char* payload);
|
||||
int __android_log_security(); /* Device Owner is present */
|
||||
|
||||
/* Retrieve the composed event buffer */
|
||||
int android_log_write_list_buffer(android_log_context ctx, const char** msg);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
|
@ -1 +0,0 @@
|
|||
../include/android
|
|
@ -1,27 +0,0 @@
|
|||
/*Special log.h file for VNDK linking modules*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Historically vendors have depended on these headers being included. */
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <log/log_id.h>
|
||||
#include <log/log_main.h>
|
||||
#include <log/log_radio.h>
|
||||
#include <log/log_read.h>
|
||||
#include <log/log_safetynet.h>
|
||||
#include <log/log_system.h>
|
||||
#include <log/log_time.h>
|
||||
|
||||
/*
|
||||
* LOG_TAG is the local tag used for the following simplified
|
||||
* logging macros. You can change this preprocessor definition
|
||||
* before using the other macros to change the tag.
|
||||
*/
|
||||
|
||||
#ifndef LOG_TAG
|
||||
#define LOG_TAG NULL
|
||||
#endif
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Special log_event_list.h file for VNDK linking modules */
|
||||
|
||||
#ifndef _LIBS_LOG_EVENT_LIST_H
|
||||
#define _LIBS_LOG_EVENT_LIST_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <log/log_id.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The opaque context used to manipulate lists of events.
|
||||
*/
|
||||
#ifndef __android_log_context_defined
|
||||
#define __android_log_context_defined
|
||||
typedef struct android_log_context_internal* android_log_context;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Creates a context associated with an event tag to write elements to
|
||||
* the list of events.
|
||||
*/
|
||||
android_log_context create_android_logger(uint32_t tag);
|
||||
|
||||
/* All lists must be braced by a begin and end call */
|
||||
/*
|
||||
* NB: If the first level braces are missing when specifying multiple
|
||||
* elements, we will manufacturer a list to embrace it for your API
|
||||
* convenience. For a single element, it will remain solitary.
|
||||
*/
|
||||
int android_log_write_list_begin(android_log_context ctx);
|
||||
int android_log_write_list_end(android_log_context ctx);
|
||||
|
||||
int android_log_write_int32(android_log_context ctx, int32_t value);
|
||||
int android_log_write_int64(android_log_context ctx, int64_t value);
|
||||
int android_log_write_string8(android_log_context ctx, const char* value);
|
||||
int android_log_write_string8_len(android_log_context ctx, const char* value,
|
||||
size_t maxlen);
|
||||
int android_log_write_float32(android_log_context ctx, float value);
|
||||
|
||||
/* Submit the composed list context to the specified logger id */
|
||||
/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
|
||||
int android_log_write_list(android_log_context ctx, log_id_t id);
|
||||
|
||||
/* Reset writer context */
|
||||
int android_log_reset(android_log_context ctx);
|
||||
|
||||
/* Reset reader context */
|
||||
int android_log_parser_reset(android_log_context ctx,
|
||||
const char* msg, size_t len);
|
||||
|
||||
/* Finished with reader or writer context */
|
||||
int android_log_destroy(android_log_context* ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBS_LOG_EVENT_LIST_H */
|
|
@ -1 +0,0 @@
|
|||
../../include/log/log_id.h
|
|
@ -1 +0,0 @@
|
|||
../../include/log/log_main.h
|
|
@ -1 +0,0 @@
|
|||
../../include/log/log_properties.h
|
|
@ -1 +0,0 @@
|
|||
../../include/log/log_radio.h
|
|
@ -1 +0,0 @@
|
|||
../../include/log/log_read.h
|
|
@ -1 +0,0 @@
|
|||
../../include/log/log_safetynet.h
|
|
@ -1 +0,0 @@
|
|||
../../include/log/log_system.h
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBS_LOG_LOG_TIME_H
|
||||
#define _LIBS_LOG_LOG_TIME_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* struct log_time is a wire-format variant of struct timespec */
|
||||
#ifndef NS_PER_SEC
|
||||
#define NS_PER_SEC 1000000000ULL
|
||||
#endif
|
||||
#ifndef US_PER_SEC
|
||||
#define US_PER_SEC 1000000ULL
|
||||
#endif
|
||||
#ifndef MS_PER_SEC
|
||||
#define MS_PER_SEC 1000ULL
|
||||
#endif
|
||||
|
||||
#ifndef __struct_log_time_defined
|
||||
#define __struct_log_time_defined
|
||||
|
||||
#define LOG_TIME_SEC(t) ((t)->tv_sec)
|
||||
/* next power of two after NS_PER_SEC */
|
||||
#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
|
||||
|
||||
typedef struct log_time {
|
||||
uint32_t tv_sec;
|
||||
uint32_t tv_nsec;
|
||||
} __attribute__((__packed__)) log_time;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _LIBS_LOG_LOG_TIME_H */
|
|
@ -1,93 +0,0 @@
|
|||
LIBLOG {
|
||||
global:
|
||||
android_name_to_log_id; # apex llndk
|
||||
android_log_id_to_name; # llndk
|
||||
__android_log_assert;
|
||||
__android_log_buf_print;
|
||||
__android_log_buf_write;
|
||||
__android_log_print;
|
||||
__android_log_vprint;
|
||||
__android_log_write;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
|
||||
LIBLOG_L {
|
||||
global:
|
||||
android_logger_clear; # llndk
|
||||
android_logger_get_id; # llndk
|
||||
android_logger_get_log_readable_size; # llndk
|
||||
android_logger_get_log_version; # llndk
|
||||
android_logger_get_log_size; # llndk
|
||||
android_logger_list_alloc; # apex llndk
|
||||
android_logger_list_alloc_time; # apex llndk
|
||||
android_logger_list_free; # apex llndk
|
||||
android_logger_list_open; # apex llndk
|
||||
android_logger_list_read; # apex llndk
|
||||
android_logger_open; # apex llndk
|
||||
android_logger_set_log_size; # llndk
|
||||
};
|
||||
|
||||
LIBLOG_M {
|
||||
global:
|
||||
android_logger_get_prune_list; # llndk
|
||||
android_logger_set_prune_list; # llndk
|
||||
android_logger_get_statistics; # llndk
|
||||
__android_log_error_write; # apex llndk
|
||||
__android_log_is_loggable;
|
||||
create_android_logger; # apex llndk
|
||||
android_log_destroy; # apex llndk
|
||||
android_log_write_list_begin; # apex llndk
|
||||
android_log_write_list_end; # apex llndk
|
||||
android_log_write_int32; # apex llndk
|
||||
android_log_write_int64; # apex llndk
|
||||
android_log_write_string8; # apex llndk
|
||||
android_log_write_string8_len; # apex llndk
|
||||
android_log_write_float32; # apex llndk
|
||||
android_log_write_list; # apex llndk
|
||||
|
||||
};
|
||||
|
||||
LIBLOG_O {
|
||||
global:
|
||||
__android_log_is_loggable_len;
|
||||
__android_log_is_debuggable; # apex llndk
|
||||
};
|
||||
|
||||
LIBLOG_Q { # introduced=29
|
||||
global:
|
||||
__android_log_bswrite; # apex
|
||||
__android_log_btwrite; # apex
|
||||
__android_log_bwrite; # apex
|
||||
__android_log_close; # apex
|
||||
__android_log_security; # apex
|
||||
android_log_reset; # llndk
|
||||
android_log_parser_reset; # llndk
|
||||
};
|
||||
|
||||
LIBLOG_R { # introduced=30
|
||||
global:
|
||||
__android_log_call_aborter;
|
||||
__android_log_default_aborter;
|
||||
__android_log_get_minimum_priority;
|
||||
__android_log_logd_logger;
|
||||
__android_log_security_bswrite; # apex
|
||||
__android_log_set_aborter;
|
||||
__android_log_set_default_tag;
|
||||
__android_log_set_logger;
|
||||
__android_log_set_minimum_priority;
|
||||
__android_log_stderr_logger;
|
||||
__android_log_write_log_message;
|
||||
};
|
||||
|
||||
LIBLOG_PRIVATE {
|
||||
global:
|
||||
__android_log_pmsg_file_read;
|
||||
__android_log_pmsg_file_write;
|
||||
android_openEventTagMap;
|
||||
android_log_processBinaryLogBuffer;
|
||||
android_log_processLogBuffer;
|
||||
android_log_read_next;
|
||||
android_log_write_list_buffer;
|
||||
create_android_log_parser;
|
||||
};
|
|
@ -1,543 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <log/log_event_list.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
|
||||
|
||||
enum ReadWriteFlag {
|
||||
kAndroidLoggerRead = 1,
|
||||
kAndroidLoggerWrite = 2,
|
||||
};
|
||||
|
||||
struct android_log_context_internal {
|
||||
uint32_t tag;
|
||||
unsigned pos; /* Read/write position into buffer */
|
||||
unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */
|
||||
unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */
|
||||
unsigned list_nest_depth;
|
||||
unsigned len; /* Length or raw buffer. */
|
||||
bool overflow;
|
||||
bool list_stop; /* next call decrement list_nest_depth and issue a stop */
|
||||
ReadWriteFlag read_write_flag;
|
||||
uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
|
||||
};
|
||||
|
||||
static void init_context(android_log_context_internal* context, uint32_t tag) {
|
||||
context->tag = tag;
|
||||
context->read_write_flag = kAndroidLoggerWrite;
|
||||
size_t needed = sizeof(android_event_list_t);
|
||||
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
|
||||
context->overflow = true;
|
||||
}
|
||||
/* Everything is a list */
|
||||
context->storage[context->pos + 0] = EVENT_TYPE_LIST;
|
||||
context->list[0] = context->pos + 1;
|
||||
context->pos += needed;
|
||||
}
|
||||
|
||||
static void init_parser_context(android_log_context_internal* context, const char* msg,
|
||||
size_t len) {
|
||||
len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
|
||||
context->len = len;
|
||||
memcpy(context->storage, msg, len);
|
||||
context->read_write_flag = kAndroidLoggerRead;
|
||||
}
|
||||
|
||||
android_log_context create_android_logger(uint32_t tag) {
|
||||
android_log_context_internal* context;
|
||||
|
||||
context =
|
||||
static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
|
||||
if (!context) {
|
||||
return NULL;
|
||||
}
|
||||
init_context(context, tag);
|
||||
|
||||
return (android_log_context)context;
|
||||
}
|
||||
|
||||
android_log_context create_android_log_parser(const char* msg, size_t len) {
|
||||
android_log_context_internal* context;
|
||||
|
||||
context =
|
||||
static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
|
||||
if (!context) {
|
||||
return NULL;
|
||||
}
|
||||
init_parser_context(context, msg, len);
|
||||
|
||||
return (android_log_context)context;
|
||||
}
|
||||
|
||||
int android_log_destroy(android_log_context* ctx) {
|
||||
android_log_context_internal* context;
|
||||
|
||||
context = (android_log_context_internal*)*ctx;
|
||||
if (!context) {
|
||||
return -EBADF;
|
||||
}
|
||||
memset(context, 0, sizeof(*context));
|
||||
free(context);
|
||||
*ctx = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_log_reset(android_log_context context) {
|
||||
uint32_t tag;
|
||||
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
tag = context->tag;
|
||||
memset(context, 0, sizeof(*context));
|
||||
init_context(context, tag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_log_parser_reset(android_log_context context, const char* msg, size_t len) {
|
||||
if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
memset(context, 0, sizeof(*context));
|
||||
init_parser_context(context, msg, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_log_write_list_begin(android_log_context context) {
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
|
||||
context->overflow = true;
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
size_t needed = sizeof(android_event_list_t);
|
||||
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
|
||||
context->overflow = true;
|
||||
return -EIO;
|
||||
}
|
||||
context->count[context->list_nest_depth]++;
|
||||
context->list_nest_depth++;
|
||||
if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
|
||||
context->overflow = true;
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
if (context->overflow) {
|
||||
return -EIO;
|
||||
}
|
||||
auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[context->pos]);
|
||||
event_list->type = EVENT_TYPE_LIST;
|
||||
event_list->element_count = 0;
|
||||
context->list[context->list_nest_depth] = context->pos + 1;
|
||||
context->count[context->list_nest_depth] = 0;
|
||||
context->pos += needed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_log_write_int32(android_log_context context, int32_t value) {
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
if (context->overflow) {
|
||||
return -EIO;
|
||||
}
|
||||
size_t needed = sizeof(android_event_int_t);
|
||||
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
|
||||
context->overflow = true;
|
||||
return -EIO;
|
||||
}
|
||||
context->count[context->list_nest_depth]++;
|
||||
auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[context->pos]);
|
||||
event_int->type = EVENT_TYPE_INT;
|
||||
event_int->data = value;
|
||||
context->pos += needed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_log_write_int64(android_log_context context, int64_t value) {
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
if (context->overflow) {
|
||||
return -EIO;
|
||||
}
|
||||
size_t needed = sizeof(android_event_long_t);
|
||||
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
|
||||
context->overflow = true;
|
||||
return -EIO;
|
||||
}
|
||||
context->count[context->list_nest_depth]++;
|
||||
auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[context->pos]);
|
||||
event_long->type = EVENT_TYPE_LONG;
|
||||
event_long->data = value;
|
||||
context->pos += needed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_log_write_string8_len(android_log_context context, const char* value, size_t maxlen) {
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
if (context->overflow) {
|
||||
return -EIO;
|
||||
}
|
||||
if (!value) {
|
||||
value = "";
|
||||
}
|
||||
int32_t len = strnlen(value, maxlen);
|
||||
size_t needed = sizeof(android_event_string_t) + len;
|
||||
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
|
||||
/* Truncate string for delivery */
|
||||
len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
|
||||
if (len <= 0) {
|
||||
context->overflow = true;
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
context->count[context->list_nest_depth]++;
|
||||
auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[context->pos]);
|
||||
event_string->type = EVENT_TYPE_STRING;
|
||||
event_string->length = len;
|
||||
if (len) {
|
||||
memcpy(&event_string->data, value, len);
|
||||
}
|
||||
context->pos += needed;
|
||||
return len;
|
||||
}
|
||||
|
||||
int android_log_write_string8(android_log_context ctx, const char* value) {
|
||||
return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
|
||||
}
|
||||
|
||||
int android_log_write_float32(android_log_context context, float value) {
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
if (context->overflow) {
|
||||
return -EIO;
|
||||
}
|
||||
size_t needed = sizeof(android_event_float_t);
|
||||
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
|
||||
context->overflow = true;
|
||||
return -EIO;
|
||||
}
|
||||
context->count[context->list_nest_depth]++;
|
||||
auto* event_float = reinterpret_cast<android_event_float_t*>(&context->storage[context->pos]);
|
||||
event_float->type = EVENT_TYPE_FLOAT;
|
||||
event_float->data = value;
|
||||
context->pos += needed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_log_write_list_end(android_log_context context) {
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
|
||||
context->overflow = true;
|
||||
context->list_nest_depth--;
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
if (!context->list_nest_depth) {
|
||||
context->overflow = true;
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
if (context->list[context->list_nest_depth] <= 0) {
|
||||
context->list_nest_depth--;
|
||||
context->overflow = true;
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
context->storage[context->list[context->list_nest_depth]] =
|
||||
context->count[context->list_nest_depth];
|
||||
context->list_nest_depth--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Logs the list of elements to the event log.
|
||||
*/
|
||||
int android_log_write_list(android_log_context context, log_id_t id) {
|
||||
const char* msg;
|
||||
ssize_t len;
|
||||
|
||||
if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
if (context->list_nest_depth) {
|
||||
return -EIO;
|
||||
}
|
||||
/* NB: if there was overflow, then log is truncated. Nothing reported */
|
||||
context->storage[1] = context->count[0];
|
||||
len = context->len = context->pos;
|
||||
msg = (const char*)context->storage;
|
||||
/* it's not a list */
|
||||
if (context->count[0] <= 1) {
|
||||
len -= sizeof(uint8_t) + sizeof(uint8_t);
|
||||
if (len < 0) {
|
||||
len = 0;
|
||||
}
|
||||
msg += sizeof(uint8_t) + sizeof(uint8_t);
|
||||
}
|
||||
return (id == LOG_ID_EVENTS)
|
||||
? __android_log_bwrite(context->tag, msg, len)
|
||||
: ((id == LOG_ID_STATS) ? __android_log_stats_bwrite(context->tag, msg, len)
|
||||
: __android_log_security_bwrite(context->tag, msg, len));
|
||||
}
|
||||
|
||||
int android_log_write_list_buffer(android_log_context context, const char** buffer) {
|
||||
const char* msg;
|
||||
ssize_t len;
|
||||
|
||||
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
|
||||
return -EBADF;
|
||||
}
|
||||
if (context->list_nest_depth) {
|
||||
return -EIO;
|
||||
}
|
||||
if (buffer == NULL) {
|
||||
return -EFAULT;
|
||||
}
|
||||
/* NB: if there was overflow, then log is truncated. Nothing reported */
|
||||
context->storage[1] = context->count[0];
|
||||
len = context->len = context->pos;
|
||||
msg = (const char*)context->storage;
|
||||
/* it's not a list */
|
||||
if (context->count[0] <= 1) {
|
||||
len -= sizeof(uint8_t) + sizeof(uint8_t);
|
||||
if (len < 0) {
|
||||
len = 0;
|
||||
}
|
||||
msg += sizeof(uint8_t) + sizeof(uint8_t);
|
||||
}
|
||||
*buffer = msg;
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
|
||||
* If there is nothing to process, the complete field is set to non-zero. If
|
||||
* an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
|
||||
* this and continues to call this function, the behavior is undefined
|
||||
* (although it won't crash).
|
||||
*/
|
||||
static android_log_list_element android_log_read_next_internal(android_log_context context,
|
||||
int peek) {
|
||||
android_log_list_element elem;
|
||||
unsigned pos;
|
||||
|
||||
memset(&elem, 0, sizeof(elem));
|
||||
|
||||
/* Nothing to parse from this context, so return complete. */
|
||||
if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
|
||||
(context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
|
||||
(context->count[context->list_nest_depth] >=
|
||||
(MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
if (context &&
|
||||
(context->list_stop || ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
|
||||
!context->count[context->list_nest_depth]))) {
|
||||
elem.type = EVENT_TYPE_LIST_STOP;
|
||||
}
|
||||
elem.complete = true;
|
||||
return elem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a different variable to update the position in case this
|
||||
* operation is a "peek".
|
||||
*/
|
||||
pos = context->pos;
|
||||
if (context->list_stop) {
|
||||
elem.type = EVENT_TYPE_LIST_STOP;
|
||||
elem.complete = !context->count[0] && (!context->list_nest_depth ||
|
||||
((context->list_nest_depth == 1) && !context->count[1]));
|
||||
if (!peek) {
|
||||
/* Suck in superfluous stop */
|
||||
if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
|
||||
context->pos = pos + 1;
|
||||
}
|
||||
if (context->list_nest_depth) {
|
||||
--context->list_nest_depth;
|
||||
if (context->count[context->list_nest_depth]) {
|
||||
context->list_stop = false;
|
||||
}
|
||||
} else {
|
||||
context->list_stop = false;
|
||||
}
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
if ((pos + 1) > context->len) {
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
elem.complete = true;
|
||||
return elem;
|
||||
}
|
||||
|
||||
elem.type = static_cast<AndroidEventLogType>(context->storage[pos]);
|
||||
switch ((int)elem.type) {
|
||||
case EVENT_TYPE_FLOAT:
|
||||
/* Rely on union to translate elem.data.int32 into elem.data.float32 */
|
||||
/* FALLTHRU */
|
||||
case EVENT_TYPE_INT: {
|
||||
elem.len = sizeof(int32_t);
|
||||
if ((pos + sizeof(android_event_int_t)) > context->len) {
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
return elem;
|
||||
}
|
||||
|
||||
auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[pos]);
|
||||
pos += sizeof(android_event_int_t);
|
||||
elem.data.int32 = event_int->data;
|
||||
/* common tangeable object suffix */
|
||||
elem.complete = !context->list_nest_depth && !context->count[0];
|
||||
if (!peek) {
|
||||
if (!context->count[context->list_nest_depth] ||
|
||||
!--(context->count[context->list_nest_depth])) {
|
||||
context->list_stop = true;
|
||||
}
|
||||
context->pos = pos;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
case EVENT_TYPE_LONG: {
|
||||
elem.len = sizeof(int64_t);
|
||||
if ((pos + sizeof(android_event_long_t)) > context->len) {
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
return elem;
|
||||
}
|
||||
|
||||
auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[pos]);
|
||||
pos += sizeof(android_event_long_t);
|
||||
elem.data.int64 = event_long->data;
|
||||
/* common tangeable object suffix */
|
||||
elem.complete = !context->list_nest_depth && !context->count[0];
|
||||
if (!peek) {
|
||||
if (!context->count[context->list_nest_depth] ||
|
||||
!--(context->count[context->list_nest_depth])) {
|
||||
context->list_stop = true;
|
||||
}
|
||||
context->pos = pos;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
case EVENT_TYPE_STRING: {
|
||||
if ((pos + sizeof(android_event_string_t)) > context->len) {
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
elem.complete = true;
|
||||
return elem;
|
||||
}
|
||||
auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[pos]);
|
||||
pos += sizeof(android_event_string_t);
|
||||
// Wire format is int32_t, but elem.len is uint16_t...
|
||||
if (event_string->length >= UINT16_MAX) {
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
return elem;
|
||||
}
|
||||
elem.len = event_string->length;
|
||||
if ((pos + elem.len) > context->len) {
|
||||
elem.len = context->len - pos; /* truncate string */
|
||||
elem.complete = true;
|
||||
if (!elem.len) {
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
elem.data.string = event_string->data;
|
||||
/* common tangeable object suffix */
|
||||
pos += elem.len;
|
||||
elem.complete = !context->list_nest_depth && !context->count[0];
|
||||
if (!peek) {
|
||||
if (!context->count[context->list_nest_depth] ||
|
||||
!--(context->count[context->list_nest_depth])) {
|
||||
context->list_stop = true;
|
||||
}
|
||||
context->pos = pos;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
case EVENT_TYPE_LIST: {
|
||||
if ((pos + sizeof(android_event_list_t)) > context->len) {
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
elem.complete = true;
|
||||
return elem;
|
||||
}
|
||||
auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[pos]);
|
||||
pos += sizeof(android_event_list_t);
|
||||
elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
|
||||
if (peek) {
|
||||
return elem;
|
||||
}
|
||||
if (context->count[context->list_nest_depth]) {
|
||||
context->count[context->list_nest_depth]--;
|
||||
}
|
||||
context->list_stop = event_list->element_count == 0;
|
||||
context->list_nest_depth++;
|
||||
if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
|
||||
context->count[context->list_nest_depth] = event_list->element_count;
|
||||
}
|
||||
context->pos = pos;
|
||||
return elem;
|
||||
}
|
||||
|
||||
case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
|
||||
pos++;
|
||||
if (!peek) {
|
||||
context->pos = pos;
|
||||
}
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
elem.complete = !context->list_nest_depth;
|
||||
if (context->list_nest_depth > 0) {
|
||||
elem.type = EVENT_TYPE_LIST_STOP;
|
||||
if (!peek) {
|
||||
context->list_nest_depth--;
|
||||
}
|
||||
}
|
||||
return elem;
|
||||
|
||||
default:
|
||||
elem.type = EVENT_TYPE_UNKNOWN;
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
android_log_list_element android_log_read_next(android_log_context ctx) {
|
||||
return android_log_read_next_internal(ctx, 0);
|
||||
}
|
||||
|
||||
android_log_list_element android_log_peek_next(android_log_context ctx) {
|
||||
return android_log_read_next_internal(ctx, 1);
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <log/log.h>
|
||||
#include <log/log_event_list.h>
|
||||
|
||||
#define MAX_SUBTAG_LEN 32
|
||||
|
||||
int __android_log_error_write(int tag, const char* subTag, int32_t uid, const char* data,
|
||||
uint32_t dataLen) {
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (subTag && (data || !dataLen)) {
|
||||
android_log_context ctx = create_android_logger(tag);
|
||||
|
||||
ret = -ENOMEM;
|
||||
if (ctx) {
|
||||
ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
|
||||
if (ret >= 0) {
|
||||
ret = android_log_write_int32(ctx, uid);
|
||||
if (ret >= 0) {
|
||||
ret = android_log_write_string8_len(ctx, data, dataLen);
|
||||
if (ret >= 0) {
|
||||
ret = android_log_write_list(ctx, LOG_ID_EVENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
android_log_destroy(&ctx);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <private/android_logger.h>
|
||||
|
||||
// Add %#q for fractional seconds to standard strptime function
|
||||
char* log_time::strptime(const char* s, const char* format) {
|
||||
time_t now;
|
||||
#ifdef __linux__
|
||||
*this = log_time(CLOCK_REALTIME);
|
||||
now = tv_sec;
|
||||
#else
|
||||
time(&now);
|
||||
tv_sec = now;
|
||||
tv_nsec = 0;
|
||||
#endif
|
||||
|
||||
struct tm* ptm;
|
||||
#if !defined(_WIN32)
|
||||
struct tm tmBuf;
|
||||
ptm = localtime_r(&now, &tmBuf);
|
||||
#else
|
||||
ptm = localtime(&now);
|
||||
#endif
|
||||
|
||||
char fmt[strlen(format) + 1];
|
||||
strcpy(fmt, format);
|
||||
|
||||
char* ret = const_cast<char*>(s);
|
||||
char* cp;
|
||||
for (char* f = cp = fmt;; ++cp) {
|
||||
if (!*cp) {
|
||||
if (f != cp) {
|
||||
ret = ::strptime(ret, f, ptm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (*cp != '%') {
|
||||
continue;
|
||||
}
|
||||
char* e = cp;
|
||||
++e;
|
||||
#if (defined(__BIONIC__))
|
||||
if (*e == 's') {
|
||||
*cp = '\0';
|
||||
if (*f) {
|
||||
ret = ::strptime(ret, f, ptm);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tv_sec = 0;
|
||||
while (isdigit(*ret)) {
|
||||
tv_sec = tv_sec * 10 + *ret - '0';
|
||||
++ret;
|
||||
}
|
||||
now = tv_sec;
|
||||
#if !defined(_WIN32)
|
||||
ptm = localtime_r(&now, &tmBuf);
|
||||
#else
|
||||
ptm = localtime(&now);
|
||||
#endif
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
unsigned num = 0;
|
||||
while (isdigit(*e)) {
|
||||
num = num * 10 + *e - '0';
|
||||
++e;
|
||||
}
|
||||
if (*e != 'q') {
|
||||
continue;
|
||||
}
|
||||
*cp = '\0';
|
||||
if (*f) {
|
||||
ret = ::strptime(ret, f, ptm);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsigned long mul = NS_PER_SEC;
|
||||
if (num == 0) {
|
||||
num = INT_MAX;
|
||||
}
|
||||
tv_nsec = 0;
|
||||
while (isdigit(*ret) && num && (mul > 1)) {
|
||||
--num;
|
||||
mul /= 10;
|
||||
tv_nsec = tv_nsec + (*ret - '0') * mul;
|
||||
++ret;
|
||||
}
|
||||
}
|
||||
f = cp = e;
|
||||
++f;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
tv_sec = mktime(ptm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Upon error, place a known value into the class, the current time.
|
||||
#ifdef __linux__
|
||||
*this = log_time(CLOCK_REALTIME);
|
||||
#else
|
||||
time(&now);
|
||||
tv_sec = now;
|
||||
tv_nsec = 0;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
|
@ -1,389 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "logd_reader.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <poll.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/parseint.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
|
||||
// O_CLOEXEC is always set.
|
||||
static int socket_local_client(const std::string& name, int type, bool timeout) {
|
||||
sockaddr_un addr = {.sun_family = AF_LOCAL};
|
||||
|
||||
std::string path = "/dev/socket/" + name;
|
||||
if (path.size() + 1 > sizeof(addr.sun_path)) {
|
||||
return -1;
|
||||
}
|
||||
strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
|
||||
|
||||
int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
// Sending and receiving messages should be instantaneous, but we don't want to wait forever if
|
||||
// logd is hung, so we set a gracious 2s timeout.
|
||||
struct timeval t = {2, 0};
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* worker for sending the command to the logger */
|
||||
ssize_t SendLogdControlMessage(char* buf, size_t buf_size) {
|
||||
ssize_t ret;
|
||||
size_t len;
|
||||
char* cp;
|
||||
int errno_save = 0;
|
||||
int sock = socket_local_client("logd", SOCK_STREAM, true);
|
||||
if (sock < 0) {
|
||||
return sock;
|
||||
}
|
||||
|
||||
len = strlen(buf) + 1;
|
||||
ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
|
||||
if (ret <= 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
len = buf_size;
|
||||
cp = buf;
|
||||
while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
|
||||
struct pollfd p;
|
||||
|
||||
if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
|
||||
break;
|
||||
}
|
||||
|
||||
len -= ret;
|
||||
cp += ret;
|
||||
|
||||
memset(&p, 0, sizeof(p));
|
||||
p.fd = sock;
|
||||
p.events = POLLIN;
|
||||
|
||||
/* Give other side 20ms to refill pipe */
|
||||
ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
|
||||
|
||||
if (ret <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(p.revents & POLLIN)) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
ret += buf_size - len;
|
||||
}
|
||||
|
||||
done:
|
||||
if ((ret == -1) && errno) {
|
||||
errno_save = errno;
|
||||
}
|
||||
close(sock);
|
||||
if (errno_save) {
|
||||
errno = errno_save;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_log_success(char* buf, ssize_t ret) {
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (strncmp(buf, "success", 7)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_logger_clear(struct logger* logger) {
|
||||
if (!android_logger_is_logd(logger)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
uint32_t log_id = android_logger_get_id(logger);
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), "clear %" PRIu32, log_id);
|
||||
|
||||
return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
enum class LogSizeType : uint32_t {
|
||||
kAllotted = 0,
|
||||
kReadable,
|
||||
kConsumed,
|
||||
};
|
||||
|
||||
static long GetLogSize(struct logger* logger, LogSizeType type) {
|
||||
if (!android_logger_is_logd(logger)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t log_id = android_logger_get_id(logger);
|
||||
char buf[512];
|
||||
switch (type) {
|
||||
case LogSizeType::kAllotted:
|
||||
snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
|
||||
break;
|
||||
case LogSizeType::kReadable:
|
||||
snprintf(buf, sizeof(buf), "getLogSizeReadable %" PRIu32, log_id);
|
||||
break;
|
||||
case LogSizeType::kConsumed:
|
||||
snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
long size;
|
||||
if (!android::base::ParseInt(buf, &size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
long android_logger_get_log_size(struct logger* logger) {
|
||||
return GetLogSize(logger, LogSizeType::kAllotted);
|
||||
}
|
||||
|
||||
long android_logger_get_log_readable_size(struct logger* logger) {
|
||||
return GetLogSize(logger, LogSizeType::kReadable);
|
||||
}
|
||||
|
||||
long android_logger_get_log_consumed_size(struct logger* logger) {
|
||||
return GetLogSize(logger, LogSizeType::kConsumed);
|
||||
}
|
||||
|
||||
int android_logger_set_log_size(struct logger* logger, unsigned long size) {
|
||||
if (!android_logger_is_logd(logger)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t log_id = android_logger_get_id(logger);
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
|
||||
|
||||
return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
int android_logger_get_log_version(struct logger*) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
|
||||
if (logger_list->mode & ANDROID_LOG_PSTORE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
char* cp = buf;
|
||||
size_t remaining = len;
|
||||
size_t n;
|
||||
|
||||
n = snprintf(cp, remaining, "getStatistics");
|
||||
n = MIN(n, remaining);
|
||||
remaining -= n;
|
||||
cp += n;
|
||||
|
||||
for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
|
||||
if ((1 << log_id) & logger_list->log_mask) {
|
||||
n = snprintf(cp, remaining, " %zu", log_id);
|
||||
n = MIN(n, remaining);
|
||||
remaining -= n;
|
||||
cp += n;
|
||||
}
|
||||
}
|
||||
|
||||
if (logger_list->pid) {
|
||||
snprintf(cp, remaining, " pid=%u", logger_list->pid);
|
||||
}
|
||||
|
||||
return SendLogdControlMessage(buf, len);
|
||||
}
|
||||
ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
|
||||
if (logger_list->mode & ANDROID_LOG_PSTORE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(buf, len, "getPruneList");
|
||||
return SendLogdControlMessage(buf, len);
|
||||
}
|
||||
|
||||
int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len) {
|
||||
if (logger_list->mode & ANDROID_LOG_PSTORE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
std::string cmd = "setPruneList " + std::string{buf, len};
|
||||
|
||||
return check_log_success(cmd.data(), SendLogdControlMessage(cmd.data(), cmd.size()));
|
||||
}
|
||||
|
||||
static int logdOpen(struct logger_list* logger_list) {
|
||||
char buffer[256], *cp, c;
|
||||
int ret, remaining, sock;
|
||||
|
||||
sock = atomic_load(&logger_list->fd);
|
||||
if (sock > 0) {
|
||||
return sock;
|
||||
}
|
||||
|
||||
sock = socket_local_client("logdr", SOCK_SEQPACKET, false);
|
||||
if (sock <= 0) {
|
||||
if ((sock == -1) && errno) {
|
||||
return -errno;
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
|
||||
cp = buffer + strlen(buffer);
|
||||
|
||||
strcpy(cp, " lids");
|
||||
cp += 5;
|
||||
c = '=';
|
||||
remaining = sizeof(buffer) - (cp - buffer);
|
||||
|
||||
for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
|
||||
if ((1 << log_id) & logger_list->log_mask) {
|
||||
ret = snprintf(cp, remaining, "%c%zu", c, log_id);
|
||||
ret = MIN(ret, remaining);
|
||||
remaining -= ret;
|
||||
cp += ret;
|
||||
c = ',';
|
||||
}
|
||||
}
|
||||
|
||||
if (logger_list->tail) {
|
||||
ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
|
||||
ret = MIN(ret, remaining);
|
||||
remaining -= ret;
|
||||
cp += ret;
|
||||
}
|
||||
|
||||
if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
|
||||
if (logger_list->mode & ANDROID_LOG_WRAP) {
|
||||
// ToDo: alternate API to allow timeout to be adjusted.
|
||||
ret = snprintf(cp, remaining, " timeout=%u", ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
|
||||
ret = MIN(ret, remaining);
|
||||
remaining -= ret;
|
||||
cp += ret;
|
||||
}
|
||||
ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, logger_list->start.tv_sec,
|
||||
logger_list->start.tv_nsec);
|
||||
ret = MIN(ret, remaining);
|
||||
remaining -= ret;
|
||||
cp += ret;
|
||||
}
|
||||
|
||||
if (logger_list->pid) {
|
||||
ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
|
||||
ret = MIN(ret, remaining);
|
||||
cp += ret;
|
||||
}
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(write(sock, buffer, cp - buffer));
|
||||
int write_errno = errno;
|
||||
|
||||
if (ret <= 0) {
|
||||
close(sock);
|
||||
if (ret == -1) {
|
||||
return -write_errno;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = atomic_exchange(&logger_list->fd, sock);
|
||||
if ((ret > 0) && (ret != sock)) {
|
||||
close(ret);
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
/* Read from the selected logs */
|
||||
int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg) {
|
||||
int ret = logdOpen(logger_list);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
|
||||
ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0));
|
||||
if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ret == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Close all the logs */
|
||||
void LogdClose(struct logger_list* logger_list) {
|
||||
int sock = atomic_exchange(&logger_list->fd, -1);
|
||||
if (sock > 0) {
|
||||
close(sock);
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "log/log_read.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg);
|
||||
void LogdClose(struct logger_list* logger_list);
|
||||
|
||||
ssize_t SendLogdControlMessage(char* buf, size_t buf_size);
|
||||
|
||||
__END_DECLS
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "logd_writer.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <poll.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "uio.h"
|
||||
|
||||
static atomic_int logd_socket;
|
||||
|
||||
// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
|
||||
// function is used to reconnect to logd without requiring a new socket.
|
||||
static void LogdConnect() {
|
||||
sockaddr_un un = {};
|
||||
un.sun_family = AF_UNIX;
|
||||
strcpy(un.sun_path, "/dev/socket/logdw");
|
||||
TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
|
||||
}
|
||||
|
||||
// logd_socket should only be opened once. If we see that logd_socket is uninitialized, we create a
|
||||
// new socket and attempt to exchange it into the atomic logd_socket. If the compare/exchange was
|
||||
// successful, then that will be the socket used for the duration of the program, otherwise a
|
||||
// different thread has already opened and written the socket to the atomic, so close the new socket
|
||||
// and return.
|
||||
static void GetSocket() {
|
||||
if (logd_socket != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
|
||||
if (new_socket <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int uninitialized_value = 0;
|
||||
if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
|
||||
close(new_socket);
|
||||
return;
|
||||
}
|
||||
|
||||
LogdConnect();
|
||||
}
|
||||
|
||||
// This is the one exception to the above. Zygote uses this to clean up open FD's after fork() and
|
||||
// before specialization. It is single threaded at this point and therefore this function is
|
||||
// explicitly not thread safe. It sets logd_socket to 0, so future logs will be safely initialized
|
||||
// whenever they happen.
|
||||
void LogdClose() {
|
||||
if (logd_socket > 0) {
|
||||
close(logd_socket);
|
||||
}
|
||||
logd_socket = 0;
|
||||
}
|
||||
|
||||
int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
|
||||
ssize_t ret;
|
||||
static const unsigned headerLength = 1;
|
||||
struct iovec newVec[nr + headerLength];
|
||||
android_log_header_t header;
|
||||
size_t i, payloadSize;
|
||||
|
||||
GetSocket();
|
||||
|
||||
if (logd_socket <= 0) {
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
/* logd, after initialization and priv drop */
|
||||
if (getuid() == AID_LOGD) {
|
||||
/*
|
||||
* ignore log messages we send to ourself (logd).
|
||||
* Such log messages are often generated by libraries we depend on
|
||||
* which use standard Android logging.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
header.id = logId;
|
||||
header.tid = gettid();
|
||||
header.realtime.tv_sec = ts->tv_sec;
|
||||
header.realtime.tv_nsec = ts->tv_nsec;
|
||||
|
||||
newVec[0].iov_base = (unsigned char*)&header;
|
||||
newVec[0].iov_len = sizeof(header);
|
||||
|
||||
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
|
||||
newVec[i].iov_base = vec[i - headerLength].iov_base;
|
||||
payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
|
||||
|
||||
if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
|
||||
newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
|
||||
if (newVec[i].iov_len) {
|
||||
++i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
|
||||
if (ret < 0) {
|
||||
LogdConnect();
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
|
||||
void LogdClose();
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include "uio.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct logger_list {
|
||||
atomic_int fd;
|
||||
int mode;
|
||||
unsigned int tail;
|
||||
log_time start;
|
||||
pid_t pid;
|
||||
uint32_t log_mask;
|
||||
};
|
||||
|
||||
// Format for a 'logger' entry: uintptr_t where only the bottom 32 bits are used.
|
||||
// bit 31: Set if this 'logger' is for logd.
|
||||
// bit 30: Set if this 'logger' is for pmsg
|
||||
// bits 0-2: the decimal value of the log buffer.
|
||||
// Other bits are unused.
|
||||
|
||||
#define LOGGER_LOGD (1U << 31)
|
||||
#define LOGGER_PMSG (1U << 30)
|
||||
#define LOGGER_LOG_ID_MASK ((1U << 3) - 1)
|
||||
|
||||
inline bool android_logger_is_logd(struct logger* logger) {
|
||||
return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
|
||||
}
|
||||
|
||||
__END_DECLS
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
** Copyright 2013-2014, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
/* In the future, we would like to make this list extensible */
|
||||
static const char* LOG_NAME[LOG_ID_MAX] = {
|
||||
/* clang-format off */
|
||||
[LOG_ID_MAIN] = "main",
|
||||
[LOG_ID_RADIO] = "radio",
|
||||
[LOG_ID_EVENTS] = "events",
|
||||
[LOG_ID_SYSTEM] = "system",
|
||||
[LOG_ID_CRASH] = "crash",
|
||||
[LOG_ID_STATS] = "stats",
|
||||
[LOG_ID_SECURITY] = "security",
|
||||
[LOG_ID_KERNEL] = "kernel",
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
const char* android_log_id_to_name(log_id_t log_id) {
|
||||
if (log_id >= LOG_ID_MAX) {
|
||||
log_id = LOG_ID_MAIN;
|
||||
}
|
||||
return LOG_NAME[log_id];
|
||||
}
|
||||
|
||||
static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value,
|
||||
"log_id_t must be an uint32_t");
|
||||
|
||||
static_assert(std::is_same<std::underlying_type<android_LogPriority>::type, uint32_t>::value,
|
||||
"log_id_t must be an uint32_t");
|
||||
|
||||
log_id_t android_name_to_log_id(const char* logName) {
|
||||
const char* b;
|
||||
unsigned int ret;
|
||||
|
||||
if (!logName) {
|
||||
return static_cast<log_id_t>(LOG_ID_MAX);
|
||||
}
|
||||
|
||||
b = strrchr(logName, '/');
|
||||
if (!b) {
|
||||
b = logName;
|
||||
} else {
|
||||
++b;
|
||||
}
|
||||
|
||||
for (ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
|
||||
const char* l = LOG_NAME[ret];
|
||||
if (l && !strcmp(b, l)) {
|
||||
return static_cast<log_id_t>(ret);
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<log_id_t>(LOG_ID_MAX);
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
** Copyright 2013-2014, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include "log/log_read.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include "logd_reader.h"
|
||||
#include "logger.h"
|
||||
#include "pmsg_reader.h"
|
||||
|
||||
/* method for getting the associated sublog id */
|
||||
log_id_t android_logger_get_id(struct logger* logger) {
|
||||
return static_cast<log_id_t>(reinterpret_cast<uintptr_t>(logger) & LOGGER_LOG_ID_MASK);
|
||||
}
|
||||
|
||||
static struct logger_list* android_logger_list_alloc_internal(int mode, unsigned int tail,
|
||||
log_time start, pid_t pid) {
|
||||
auto* logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(struct logger_list)));
|
||||
if (!logger_list) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
logger_list->mode = mode;
|
||||
logger_list->start = start;
|
||||
logger_list->tail = tail;
|
||||
logger_list->pid = pid;
|
||||
|
||||
return logger_list;
|
||||
}
|
||||
|
||||
struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
|
||||
return android_logger_list_alloc_internal(mode, tail, log_time(0, 0), pid);
|
||||
}
|
||||
|
||||
struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
|
||||
return android_logger_list_alloc_internal(mode, 0, start, pid);
|
||||
}
|
||||
|
||||
/* Open the named log and add it to the logger list */
|
||||
struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
|
||||
if (!logger_list || (logId >= LOG_ID_MAX)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
logger_list->log_mask |= 1 << logId;
|
||||
|
||||
uintptr_t logger = logId;
|
||||
logger |= (logger_list->mode & ANDROID_LOG_PSTORE) ? LOGGER_PMSG : LOGGER_LOGD;
|
||||
return reinterpret_cast<struct logger*>(logger);
|
||||
}
|
||||
|
||||
/* Open the single named log and make it part of a new logger list */
|
||||
struct logger_list* android_logger_list_open(log_id_t logId, int mode, unsigned int tail,
|
||||
pid_t pid) {
|
||||
struct logger_list* logger_list = android_logger_list_alloc(mode, tail, pid);
|
||||
|
||||
if (!logger_list) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!android_logger_open(logger_list, logId)) {
|
||||
android_logger_list_free(logger_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return logger_list;
|
||||
}
|
||||
|
||||
int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
|
||||
if (logger_list == nullptr || logger_list->log_mask == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (logger_list->mode & ANDROID_LOG_PSTORE) {
|
||||
ret = PmsgRead(logger_list, log_msg);
|
||||
} else {
|
||||
ret = LogdRead(logger_list, log_msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret > LOGGER_ENTRY_MAX_LEN) {
|
||||
ret = LOGGER_ENTRY_MAX_LEN;
|
||||
}
|
||||
|
||||
if (ret < static_cast<int>(sizeof(log_msg->entry))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (log_msg->entry.hdr_size < sizeof(log_msg->entry) ||
|
||||
log_msg->entry.hdr_size >= LOGGER_ENTRY_MAX_LEN - sizeof(log_msg->entry)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (log_msg->entry.len > ret - log_msg->entry.hdr_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
log_msg->buf[log_msg->entry.len + log_msg->entry.hdr_size] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Close all the logs */
|
||||
void android_logger_list_free(struct logger_list* logger_list) {
|
||||
if (logger_list == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (logger_list->mode & ANDROID_LOG_PSTORE) {
|
||||
PmsgClose(logger_list);
|
||||
} else {
|
||||
LogdClose(logger_list);
|
||||
}
|
||||
#endif
|
||||
|
||||
free(logger_list);
|
||||
}
|
|
@ -1,519 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "logger_write.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef __BIONIC__
|
||||
#include <android/set_abort_message.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <android-base/errno_restorer.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "android/log.h"
|
||||
#include "log/log_read.h"
|
||||
#include "logger.h"
|
||||
#include "uio.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "logd_writer.h"
|
||||
#include "pmsg_writer.h"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <pthread.h>
|
||||
#elif defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <syscall.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
using android::base::ErrnoRestorer;
|
||||
|
||||
#define LOG_BUF_SIZE 1024
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
static int check_log_uid_permissions() {
|
||||
uid_t uid = getuid();
|
||||
|
||||
/* Matches clientHasLogCredentials() in logd */
|
||||
if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
|
||||
uid = geteuid();
|
||||
if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
|
||||
gid_t gid = getgid();
|
||||
if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
|
||||
gid = getegid();
|
||||
if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
|
||||
int num_groups;
|
||||
gid_t* groups;
|
||||
|
||||
num_groups = getgroups(0, NULL);
|
||||
if (num_groups <= 0) {
|
||||
return -EPERM;
|
||||
}
|
||||
groups = static_cast<gid_t*>(calloc(num_groups, sizeof(gid_t)));
|
||||
if (!groups) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
num_groups = getgroups(num_groups, groups);
|
||||
while (num_groups > 0) {
|
||||
if (groups[num_groups - 1] == AID_LOG) {
|
||||
break;
|
||||
}
|
||||
--num_groups;
|
||||
}
|
||||
free(groups);
|
||||
if (num_groups <= 0) {
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Release any logger resources. A new log write will immediately re-acquire.
|
||||
*/
|
||||
void __android_log_close() {
|
||||
#ifdef __ANDROID__
|
||||
LogdClose();
|
||||
PmsgClose();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__GLIBC__) || defined(_WIN32)
|
||||
static const char* getprogname() {
|
||||
#if defined(__GLIBC__)
|
||||
return program_invocation_short_name;
|
||||
#elif defined(_WIN32)
|
||||
static bool first = true;
|
||||
static char progname[MAX_PATH] = {};
|
||||
|
||||
if (first) {
|
||||
char path[PATH_MAX + 1];
|
||||
DWORD result = GetModuleFileName(nullptr, path, sizeof(path) - 1);
|
||||
if (result == 0 || result == sizeof(path) - 1) return "";
|
||||
path[PATH_MAX - 1] = 0;
|
||||
|
||||
char* path_basename = basename(path);
|
||||
|
||||
snprintf(progname, sizeof(progname), "%s", path_basename);
|
||||
first = false;
|
||||
}
|
||||
|
||||
return progname;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// It's possible for logging to happen during static initialization before our globals are
|
||||
// initialized, so we place this std::string in a function such that it is initialized on the first
|
||||
// call.
|
||||
std::string& GetDefaultTag() {
|
||||
static std::string default_tag = getprogname();
|
||||
return default_tag;
|
||||
}
|
||||
|
||||
void __android_log_set_default_tag(const char* tag) {
|
||||
GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
|
||||
}
|
||||
|
||||
static std::atomic_int32_t minimum_log_priority = ANDROID_LOG_DEFAULT;
|
||||
int32_t __android_log_set_minimum_priority(int32_t priority) {
|
||||
return minimum_log_priority.exchange(priority, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int32_t __android_log_get_minimum_priority() {
|
||||
return minimum_log_priority;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static __android_logger_function logger_function = __android_log_logd_logger;
|
||||
#else
|
||||
static __android_logger_function logger_function = __android_log_stderr_logger;
|
||||
#endif
|
||||
|
||||
void __android_log_set_logger(__android_logger_function logger) {
|
||||
logger_function = logger;
|
||||
}
|
||||
|
||||
void __android_log_default_aborter(const char* abort_message) {
|
||||
#ifdef __ANDROID__
|
||||
android_set_abort_message(abort_message);
|
||||
#else
|
||||
UNUSED(abort_message);
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
|
||||
static __android_aborter_function aborter_function = __android_log_default_aborter;
|
||||
|
||||
void __android_log_set_aborter(__android_aborter_function aborter) {
|
||||
aborter_function = aborter;
|
||||
}
|
||||
|
||||
void __android_log_call_aborter(const char* abort_message) {
|
||||
aborter_function(abort_message);
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
|
||||
int ret;
|
||||
struct timespec ts;
|
||||
|
||||
if (log_id == LOG_ID_KERNEL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
|
||||
if (log_id == LOG_ID_SECURITY) {
|
||||
if (vec[0].iov_len < 4) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = check_log_uid_permissions();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (!__android_log_security()) {
|
||||
/* If only we could reset downstream logd counter */
|
||||
return -EPERM;
|
||||
}
|
||||
} else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
|
||||
if (vec[0].iov_len < 4) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = LogdWrite(log_id, &ts, vec, nr);
|
||||
PmsgWrite(log_id, &ts, vec, nr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static int write_to_log(log_id_t, struct iovec*, size_t) {
|
||||
// Non-Android text logs should go to __android_log_stderr_logger, not here.
|
||||
// Non-Android binary logs are always dropped.
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Copied from base/threads.cpp
|
||||
static uint64_t GetThreadId() {
|
||||
#if defined(__BIONIC__)
|
||||
return gettid();
|
||||
#elif defined(__APPLE__)
|
||||
uint64_t tid;
|
||||
pthread_threadid_np(NULL, &tid);
|
||||
return tid;
|
||||
#elif defined(__linux__)
|
||||
return syscall(__NR_gettid);
|
||||
#elif defined(_WIN32)
|
||||
return GetCurrentThreadId();
|
||||
#endif
|
||||
}
|
||||
|
||||
void __android_log_stderr_logger(const struct __android_log_message* log_message) {
|
||||
struct tm now;
|
||||
time_t t = time(nullptr);
|
||||
|
||||
#if defined(_WIN32)
|
||||
localtime_s(&now, &t);
|
||||
#else
|
||||
localtime_r(&t, &now);
|
||||
#endif
|
||||
|
||||
char timestamp[32];
|
||||
strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
|
||||
|
||||
static const char log_characters[] = "XXVDIWEF";
|
||||
static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
|
||||
"Mismatch in size of log_characters and values in android_LogPriority");
|
||||
int32_t priority =
|
||||
log_message->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : log_message->priority;
|
||||
char priority_char = log_characters[priority];
|
||||
uint64_t tid = GetThreadId();
|
||||
|
||||
if (log_message->file != nullptr) {
|
||||
fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n",
|
||||
log_message->tag ? log_message->tag : "nullptr", priority_char, timestamp, getpid(),
|
||||
tid, log_message->file, log_message->line, log_message->message);
|
||||
} else {
|
||||
fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n",
|
||||
log_message->tag ? log_message->tag : "nullptr", priority_char, timestamp, getpid(),
|
||||
tid, log_message->message);
|
||||
}
|
||||
}
|
||||
|
||||
void __android_log_logd_logger(const struct __android_log_message* log_message) {
|
||||
int buffer_id = log_message->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : log_message->buffer_id;
|
||||
|
||||
struct iovec vec[3];
|
||||
vec[0].iov_base =
|
||||
const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&log_message->priority));
|
||||
vec[0].iov_len = 1;
|
||||
vec[1].iov_base = const_cast<void*>(static_cast<const void*>(log_message->tag));
|
||||
vec[1].iov_len = strlen(log_message->tag) + 1;
|
||||
vec[2].iov_base = const_cast<void*>(static_cast<const void*>(log_message->message));
|
||||
vec[2].iov_len = strlen(log_message->message) + 1;
|
||||
|
||||
write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
|
||||
}
|
||||
|
||||
int __android_log_write(int prio, const char* tag, const char* msg) {
|
||||
return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
|
||||
}
|
||||
|
||||
void __android_log_write_log_message(__android_log_message* log_message) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
if (log_message->buffer_id != LOG_ID_DEFAULT && log_message->buffer_id != LOG_ID_MAIN &&
|
||||
log_message->buffer_id != LOG_ID_SYSTEM && log_message->buffer_id != LOG_ID_RADIO &&
|
||||
log_message->buffer_id != LOG_ID_CRASH) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (log_message->tag == nullptr) {
|
||||
log_message->tag = GetDefaultTag().c_str();
|
||||
}
|
||||
|
||||
#if __BIONIC__
|
||||
if (log_message->priority == ANDROID_LOG_FATAL) {
|
||||
android_set_abort_message(log_message->message);
|
||||
}
|
||||
#endif
|
||||
|
||||
logger_function(log_message);
|
||||
}
|
||||
|
||||
int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
__android_log_message log_message = {
|
||||
sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, msg};
|
||||
__android_log_write_log_message(&log_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
|
||||
|
||||
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
|
||||
|
||||
__android_log_message log_message = {
|
||||
sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
|
||||
__android_log_write_log_message(&log_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
__android_log_message log_message = {
|
||||
sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
|
||||
__android_log_write_log_message(&log_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
__android_log_message log_message = {
|
||||
sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, buf};
|
||||
__android_log_write_log_message(&log_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
|
||||
__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
|
||||
|
||||
if (fmt) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
|
||||
va_end(ap);
|
||||
} else {
|
||||
/* Msg not provided, log condition. N.B. Do not use cond directly as
|
||||
* format string as it could contain spurious '%' syntax (e.g.
|
||||
* "%d" in "blocks%devs == 0").
|
||||
*/
|
||||
if (cond)
|
||||
snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
|
||||
else
|
||||
strcpy(buf, "Unspecified assertion failed");
|
||||
}
|
||||
|
||||
// Log assertion failures to stderr for the benefit of "adb shell" users
|
||||
// and gtests (http://b/23675822).
|
||||
TEMP_FAILURE_RETRY(write(2, buf, strlen(buf)));
|
||||
TEMP_FAILURE_RETRY(write(2, "\n", 1));
|
||||
|
||||
__android_log_write(ANDROID_LOG_FATAL, tag, buf);
|
||||
__android_log_call_aborter(buf);
|
||||
abort();
|
||||
}
|
||||
|
||||
int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
struct iovec vec[2];
|
||||
|
||||
vec[0].iov_base = &tag;
|
||||
vec[0].iov_len = sizeof(tag);
|
||||
vec[1].iov_base = (void*)payload;
|
||||
vec[1].iov_len = len;
|
||||
|
||||
return write_to_log(LOG_ID_EVENTS, vec, 2);
|
||||
}
|
||||
|
||||
int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
struct iovec vec[2];
|
||||
|
||||
vec[0].iov_base = &tag;
|
||||
vec[0].iov_len = sizeof(tag);
|
||||
vec[1].iov_base = (void*)payload;
|
||||
vec[1].iov_len = len;
|
||||
|
||||
return write_to_log(LOG_ID_STATS, vec, 2);
|
||||
}
|
||||
|
||||
int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
struct iovec vec[2];
|
||||
|
||||
vec[0].iov_base = &tag;
|
||||
vec[0].iov_len = sizeof(tag);
|
||||
vec[1].iov_base = (void*)payload;
|
||||
vec[1].iov_len = len;
|
||||
|
||||
return write_to_log(LOG_ID_SECURITY, vec, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Like __android_log_bwrite, but takes the type as well. Doesn't work
|
||||
* for the general case where we're generating lists of stuff, but very
|
||||
* handy if we just want to dump an integer into the log.
|
||||
*/
|
||||
int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
struct iovec vec[3];
|
||||
|
||||
vec[0].iov_base = &tag;
|
||||
vec[0].iov_len = sizeof(tag);
|
||||
vec[1].iov_base = &type;
|
||||
vec[1].iov_len = sizeof(type);
|
||||
vec[2].iov_base = (void*)payload;
|
||||
vec[2].iov_len = len;
|
||||
|
||||
return write_to_log(LOG_ID_EVENTS, vec, 3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Like __android_log_bwrite, but used for writing strings to the
|
||||
* event log.
|
||||
*/
|
||||
int __android_log_bswrite(int32_t tag, const char* payload) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
struct iovec vec[4];
|
||||
char type = EVENT_TYPE_STRING;
|
||||
uint32_t len = strlen(payload);
|
||||
|
||||
vec[0].iov_base = &tag;
|
||||
vec[0].iov_len = sizeof(tag);
|
||||
vec[1].iov_base = &type;
|
||||
vec[1].iov_len = sizeof(type);
|
||||
vec[2].iov_base = &len;
|
||||
vec[2].iov_len = sizeof(len);
|
||||
vec[3].iov_base = (void*)payload;
|
||||
vec[3].iov_len = len;
|
||||
|
||||
return write_to_log(LOG_ID_EVENTS, vec, 4);
|
||||
}
|
||||
|
||||
/*
|
||||
* Like __android_log_security_bwrite, but used for writing strings to the
|
||||
* security log.
|
||||
*/
|
||||
int __android_log_security_bswrite(int32_t tag, const char* payload) {
|
||||
ErrnoRestorer errno_restorer;
|
||||
|
||||
struct iovec vec[4];
|
||||
char type = EVENT_TYPE_STRING;
|
||||
uint32_t len = strlen(payload);
|
||||
|
||||
vec[0].iov_base = &tag;
|
||||
vec[0].iov_len = sizeof(tag);
|
||||
vec[1].iov_base = &type;
|
||||
vec[1].iov_len = sizeof(type);
|
||||
vec[2].iov_base = &len;
|
||||
vec[2].iov_len = sizeof(len);
|
||||
vec[3].iov_base = (void*)payload;
|
||||
vec[3].iov_len = len;
|
||||
|
||||
return write_to_log(LOG_ID_SECURITY, vec, 4);
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string& GetDefaultTag();
|
1748
liblog/logprint.cpp
1748
liblog/logprint.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,471 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "pmsg_reader.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cutils/list.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg) {
|
||||
ssize_t ret;
|
||||
off_t current, next;
|
||||
struct __attribute__((__packed__)) {
|
||||
android_pmsg_log_header_t p;
|
||||
android_log_header_t l;
|
||||
uint8_t prio;
|
||||
} buf;
|
||||
static uint8_t preread_count;
|
||||
|
||||
memset(log_msg, 0, sizeof(*log_msg));
|
||||
|
||||
if (atomic_load(&logger_list->fd) <= 0) {
|
||||
int i, fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
if (fd == 0) { /* Argggg */
|
||||
fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
|
||||
close(0);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
i = atomic_exchange(&logger_list->fd, fd);
|
||||
if ((i > 0) && (i != fd)) {
|
||||
close(i);
|
||||
}
|
||||
preread_count = 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int fd;
|
||||
|
||||
if (preread_count < sizeof(buf)) {
|
||||
fd = atomic_load(&logger_list->fd);
|
||||
if (fd <= 0) {
|
||||
return -EBADF;
|
||||
}
|
||||
ret = TEMP_FAILURE_RETRY(read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
}
|
||||
preread_count += ret;
|
||||
}
|
||||
if (preread_count != sizeof(buf)) {
|
||||
return preread_count ? -EIO : -EAGAIN;
|
||||
}
|
||||
if ((buf.p.magic != LOGGER_MAGIC) || (buf.p.len <= sizeof(buf)) ||
|
||||
(buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) || (buf.l.id >= LOG_ID_MAX) ||
|
||||
(buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
|
||||
((buf.l.id != LOG_ID_EVENTS) && (buf.l.id != LOG_ID_SECURITY) &&
|
||||
((buf.prio == ANDROID_LOG_UNKNOWN) || (buf.prio == ANDROID_LOG_DEFAULT) ||
|
||||
(buf.prio >= ANDROID_LOG_SILENT)))) {
|
||||
do {
|
||||
memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
|
||||
} while (preread_count && (buf.p.magic != LOGGER_MAGIC));
|
||||
continue;
|
||||
}
|
||||
preread_count = 0;
|
||||
|
||||
if ((logger_list->log_mask & (1 << buf.l.id)) &&
|
||||
((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
|
||||
((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
|
||||
((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
|
||||
(logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
|
||||
(!logger_list->pid || (logger_list->pid == buf.p.pid))) {
|
||||
char* msg = reinterpret_cast<char*>(&log_msg->entry) + sizeof(log_msg->entry);
|
||||
*msg = buf.prio;
|
||||
fd = atomic_load(&logger_list->fd);
|
||||
if (fd <= 0) {
|
||||
return -EBADF;
|
||||
}
|
||||
ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
}
|
||||
if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
log_msg->entry.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
|
||||
log_msg->entry.hdr_size = sizeof(log_msg->entry);
|
||||
log_msg->entry.pid = buf.p.pid;
|
||||
log_msg->entry.tid = buf.l.tid;
|
||||
log_msg->entry.sec = buf.l.realtime.tv_sec;
|
||||
log_msg->entry.nsec = buf.l.realtime.tv_nsec;
|
||||
log_msg->entry.lid = buf.l.id;
|
||||
log_msg->entry.uid = buf.p.uid;
|
||||
|
||||
return ret + sizeof(buf.prio) + log_msg->entry.hdr_size;
|
||||
}
|
||||
|
||||
fd = atomic_load(&logger_list->fd);
|
||||
if (fd <= 0) {
|
||||
return -EBADF;
|
||||
}
|
||||
current = TEMP_FAILURE_RETRY(lseek(fd, (off_t)0, SEEK_CUR));
|
||||
if (current < 0) {
|
||||
return -errno;
|
||||
}
|
||||
fd = atomic_load(&logger_list->fd);
|
||||
if (fd <= 0) {
|
||||
return -EBADF;
|
||||
}
|
||||
next = TEMP_FAILURE_RETRY(lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
|
||||
if (next < 0) {
|
||||
return -errno;
|
||||
}
|
||||
if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PmsgClose(struct logger_list* logger_list) {
|
||||
int fd = atomic_exchange(&logger_list->fd, 0);
|
||||
if (fd > 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void* realloc_or_free(void* ptr, size_t new_size) {
|
||||
void* result = realloc(ptr, new_size);
|
||||
if (!result) {
|
||||
free(ptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
|
||||
__android_log_pmsg_file_read_fn fn, void* arg) {
|
||||
ssize_t ret;
|
||||
struct logger_list logger_list;
|
||||
struct content {
|
||||
struct listnode node;
|
||||
struct logger_entry entry;
|
||||
} * content;
|
||||
struct names {
|
||||
struct listnode node;
|
||||
struct listnode content;
|
||||
log_id_t id;
|
||||
char prio;
|
||||
char name[];
|
||||
} * names;
|
||||
struct listnode name_list;
|
||||
struct listnode *node, *n;
|
||||
size_t len, prefix_len;
|
||||
|
||||
if (!fn) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Add just enough clues in logger_list and transp to make API function */
|
||||
memset(&logger_list, 0, sizeof(logger_list));
|
||||
|
||||
logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK;
|
||||
logger_list.log_mask = (unsigned)-1;
|
||||
if (logId != LOG_ID_ANY) {
|
||||
logger_list.log_mask = (1 << logId);
|
||||
}
|
||||
logger_list.log_mask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
|
||||
if (!logger_list.log_mask) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Initialize name list */
|
||||
list_init(&name_list);
|
||||
|
||||
ret = SSIZE_MAX;
|
||||
|
||||
/* Validate incoming prefix, shift until it contains only 0 or 1 : or / */
|
||||
prefix_len = 0;
|
||||
if (prefix) {
|
||||
const char *prev = NULL, *last = NULL, *cp = prefix;
|
||||
while ((cp = strpbrk(cp, "/:"))) {
|
||||
prev = last;
|
||||
last = cp;
|
||||
cp = cp + 1;
|
||||
}
|
||||
if (prev) {
|
||||
prefix = prev + 1;
|
||||
}
|
||||
prefix_len = strlen(prefix);
|
||||
}
|
||||
|
||||
/* Read the file content */
|
||||
log_msg log_msg;
|
||||
while (PmsgRead(&logger_list, &log_msg) > 0) {
|
||||
const char* cp;
|
||||
size_t hdr_size = log_msg.entry.hdr_size;
|
||||
|
||||
char* msg = (char*)&log_msg + hdr_size;
|
||||
const char* split = NULL;
|
||||
|
||||
if (hdr_size != sizeof(log_msg.entry)) {
|
||||
continue;
|
||||
}
|
||||
/* Check for invalid sequence number */
|
||||
if (log_msg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE ||
|
||||
(log_msg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
|
||||
ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Determine if it has <dirbase>:<filebase> format for tag */
|
||||
len = log_msg.entry.len - sizeof(prio);
|
||||
for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
|
||||
if (*cp == ':') {
|
||||
if (split) {
|
||||
break;
|
||||
}
|
||||
split = cp;
|
||||
}
|
||||
}
|
||||
if (*cp || !split) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Filters */
|
||||
if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) {
|
||||
size_t offset;
|
||||
/*
|
||||
* Allow : to be a synonym for /
|
||||
* Things we do dealing with const char * and do not alloc
|
||||
*/
|
||||
split = strchr(prefix, ':');
|
||||
if (split) {
|
||||
continue;
|
||||
}
|
||||
split = strchr(prefix, '/');
|
||||
if (!split) {
|
||||
continue;
|
||||
}
|
||||
offset = split - prefix;
|
||||
if ((msg[offset + sizeof(prio)] != ':') || strncmp(msg + sizeof(prio), prefix, offset)) {
|
||||
continue;
|
||||
}
|
||||
++offset;
|
||||
if ((prefix_len > offset) &&
|
||||
strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check if there is an existing entry */
|
||||
list_for_each(node, &name_list) {
|
||||
names = node_to_item(node, struct names, node);
|
||||
if (!strcmp(names->name, msg + sizeof(prio)) && names->id == log_msg.entry.lid &&
|
||||
names->prio == *msg) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We do not have an existing entry, create and add one */
|
||||
if (node == &name_list) {
|
||||
static const char numbers[] = "0123456789";
|
||||
unsigned long long nl;
|
||||
|
||||
len = strlen(msg + sizeof(prio)) + 1;
|
||||
names = static_cast<struct names*>(calloc(1, sizeof(*names) + len));
|
||||
if (!names) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
strcpy(names->name, msg + sizeof(prio));
|
||||
names->id = static_cast<log_id_t>(log_msg.entry.lid);
|
||||
names->prio = *msg;
|
||||
list_init(&names->content);
|
||||
/*
|
||||
* Insert in reverse numeric _then_ alpha sorted order as
|
||||
* representative of log rotation:
|
||||
*
|
||||
* log.10
|
||||
* klog.10
|
||||
* . . .
|
||||
* log.2
|
||||
* klog.2
|
||||
* log.1
|
||||
* klog.1
|
||||
* log
|
||||
* klog
|
||||
*
|
||||
* thus when we present the content, we are provided the oldest
|
||||
* first, which when 'refreshed' could spill off the end of the
|
||||
* pmsg FIFO but retaining the newest data for last with best
|
||||
* chances to survive.
|
||||
*/
|
||||
nl = 0;
|
||||
cp = strpbrk(names->name, numbers);
|
||||
if (cp) {
|
||||
nl = strtoull(cp, NULL, 10);
|
||||
}
|
||||
list_for_each_reverse(node, &name_list) {
|
||||
struct names* a_name = node_to_item(node, struct names, node);
|
||||
const char* r = a_name->name;
|
||||
int compare = 0;
|
||||
|
||||
unsigned long long nr = 0;
|
||||
cp = strpbrk(r, numbers);
|
||||
if (cp) {
|
||||
nr = strtoull(cp, NULL, 10);
|
||||
}
|
||||
if (nr != nl) {
|
||||
compare = (nl > nr) ? 1 : -1;
|
||||
}
|
||||
if (compare == 0) {
|
||||
compare = strcmp(names->name, r);
|
||||
}
|
||||
if (compare <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
list_add_head(node, &names->node);
|
||||
}
|
||||
|
||||
/* Remove any file fragments that match our sequence number */
|
||||
list_for_each_safe(node, n, &names->content) {
|
||||
content = node_to_item(node, struct content, node);
|
||||
if (log_msg.entry.nsec == content->entry.nsec) {
|
||||
list_remove(&content->node);
|
||||
free(content);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add content */
|
||||
content = static_cast<struct content*>(
|
||||
calloc(1, sizeof(content->node) + hdr_size + log_msg.entry.len));
|
||||
if (!content) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
memcpy(&content->entry, &log_msg.entry, hdr_size + log_msg.entry.len);
|
||||
|
||||
/* Insert in sequence number sorted order, to ease reconstruction */
|
||||
list_for_each_reverse(node, &names->content) {
|
||||
if ((node_to_item(node, struct content, node))->entry.nsec < log_msg.entry.nsec) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
list_add_head(node, &content->node);
|
||||
}
|
||||
PmsgClose(&logger_list);
|
||||
|
||||
/* Progress through all the collected files */
|
||||
list_for_each_safe(node, n, &name_list) {
|
||||
struct listnode *content_node, *m;
|
||||
char* buf;
|
||||
size_t sequence, tag_len;
|
||||
|
||||
names = node_to_item(node, struct names, node);
|
||||
|
||||
/* Construct content into a linear buffer */
|
||||
buf = NULL;
|
||||
len = 0;
|
||||
sequence = 0;
|
||||
tag_len = strlen(names->name) + sizeof(char); /* tag + nul */
|
||||
list_for_each_safe(content_node, m, &names->content) {
|
||||
ssize_t add_len;
|
||||
|
||||
content = node_to_item(content_node, struct content, node);
|
||||
add_len = content->entry.len - tag_len - sizeof(prio);
|
||||
if (add_len <= 0) {
|
||||
list_remove(content_node);
|
||||
free(content);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
buf = static_cast<char*>(malloc(sizeof(char)));
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
list_remove(content_node);
|
||||
free(content);
|
||||
continue;
|
||||
}
|
||||
*buf = '\0';
|
||||
}
|
||||
|
||||
/* Missing sequence numbers */
|
||||
while (sequence < content->entry.nsec) {
|
||||
/* plus space for enforced nul */
|
||||
buf = static_cast<char*>(realloc_or_free(buf, len + sizeof(char) + sizeof(char)));
|
||||
if (!buf) {
|
||||
break;
|
||||
}
|
||||
buf[len] = '\f'; /* Mark missing content with a form feed */
|
||||
buf[++len] = '\0';
|
||||
sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE;
|
||||
}
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
list_remove(content_node);
|
||||
free(content);
|
||||
continue;
|
||||
}
|
||||
/* plus space for enforced nul */
|
||||
buf = static_cast<char*>(realloc_or_free(buf, len + add_len + sizeof(char)));
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
list_remove(content_node);
|
||||
free(content);
|
||||
continue;
|
||||
}
|
||||
memcpy(buf + len, (char*)&content->entry + content->entry.hdr_size + tag_len + sizeof(prio),
|
||||
add_len);
|
||||
len += add_len;
|
||||
buf[len] = '\0'; /* enforce trailing hidden nul */
|
||||
sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE;
|
||||
|
||||
list_remove(content_node);
|
||||
free(content);
|
||||
}
|
||||
if (buf) {
|
||||
if (len) {
|
||||
/* Buffer contains enforced trailing nul just beyond length */
|
||||
ssize_t r;
|
||||
*strchr(names->name, ':') = '/'; /* Convert back to filename */
|
||||
r = (*fn)(names->id, names->prio, names->name, buf, len, arg);
|
||||
if ((ret >= 0) && (r > 0)) {
|
||||
if (ret == SSIZE_MAX) {
|
||||
ret = r;
|
||||
} else {
|
||||
ret += r;
|
||||
}
|
||||
} else if (r < ret) {
|
||||
ret = r;
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
list_remove(node);
|
||||
free(names);
|
||||
}
|
||||
return (ret == SSIZE_MAX) ? -ENOENT : ret;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "log/log_read.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg);
|
||||
void PmsgClose(struct logger_list* logger_list);
|
||||
|
||||
__END_DECLS
|
|
@ -1,247 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "pmsg_writer.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <log/log_properties.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "uio.h"
|
||||
|
||||
static atomic_int pmsg_fd;
|
||||
|
||||
// pmsg_fd should only beopened once. If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
|
||||
// then attempt to compare/exchange it into pmsg_fd. If the compare/exchange was successful, then
|
||||
// that will be the fd used for the duration of the program, otherwise a different thread has
|
||||
// already opened and written the fd to the atomic, so close the new fd and return.
|
||||
static void GetPmsgFd() {
|
||||
if (pmsg_fd != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
|
||||
if (new_fd <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int uninitialized_value = 0;
|
||||
if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
|
||||
close(new_fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PmsgClose() {
|
||||
if (pmsg_fd > 0) {
|
||||
close(pmsg_fd);
|
||||
}
|
||||
pmsg_fd = 0;
|
||||
}
|
||||
|
||||
int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
|
||||
static const unsigned headerLength = 2;
|
||||
struct iovec newVec[nr + headerLength];
|
||||
android_log_header_t header;
|
||||
android_pmsg_log_header_t pmsgHeader;
|
||||
size_t i, payloadSize;
|
||||
ssize_t ret;
|
||||
|
||||
if (!__android_log_is_debuggable()) {
|
||||
if (logId != LOG_ID_EVENTS && logId != LOG_ID_SECURITY) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (logId == LOG_ID_EVENTS) {
|
||||
if (vec[0].iov_len < 4) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetPmsgFd();
|
||||
|
||||
if (pmsg_fd <= 0) {
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
/*
|
||||
* struct {
|
||||
* // what we provide to pstore
|
||||
* android_pmsg_log_header_t pmsgHeader;
|
||||
* // what we provide to file
|
||||
* android_log_header_t header;
|
||||
* // caller provides
|
||||
* union {
|
||||
* struct {
|
||||
* char prio;
|
||||
* char payload[];
|
||||
* } string;
|
||||
* struct {
|
||||
* uint32_t tag
|
||||
* char payload[];
|
||||
* } binary;
|
||||
* };
|
||||
* };
|
||||
*/
|
||||
|
||||
pmsgHeader.magic = LOGGER_MAGIC;
|
||||
pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
|
||||
pmsgHeader.uid = getuid();
|
||||
pmsgHeader.pid = getpid();
|
||||
|
||||
header.id = logId;
|
||||
header.tid = gettid();
|
||||
header.realtime.tv_sec = ts->tv_sec;
|
||||
header.realtime.tv_nsec = ts->tv_nsec;
|
||||
|
||||
newVec[0].iov_base = (unsigned char*)&pmsgHeader;
|
||||
newVec[0].iov_len = sizeof(pmsgHeader);
|
||||
newVec[1].iov_base = (unsigned char*)&header;
|
||||
newVec[1].iov_len = sizeof(header);
|
||||
|
||||
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
|
||||
newVec[i].iov_base = vec[i - headerLength].iov_base;
|
||||
payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
|
||||
|
||||
if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
|
||||
newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
|
||||
if (newVec[i].iov_len) {
|
||||
++i;
|
||||
}
|
||||
payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pmsgHeader.len += payloadSize;
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(writev(pmsg_fd, newVec, i));
|
||||
if (ret < 0) {
|
||||
ret = errno ? -errno : -ENOTCONN;
|
||||
}
|
||||
|
||||
if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
|
||||
ret -= sizeof(header) - sizeof(pmsgHeader);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Virtual pmsg filesystem
|
||||
*
|
||||
* Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
|
||||
* maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
|
||||
* file.
|
||||
*
|
||||
* Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
|
||||
*/
|
||||
|
||||
static inline const char* strnrchr(const char* buf, size_t len, char c) {
|
||||
const char* cp = buf + len;
|
||||
while ((--cp > buf) && (*cp != c))
|
||||
;
|
||||
if (cp <= buf) {
|
||||
return buf + len;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
/* Write a buffer as filename references (tag = <basedir>:<basename>) */
|
||||
ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio, const char* filename,
|
||||
const char* buf, size_t len) {
|
||||
size_t length, packet_len;
|
||||
const char* tag;
|
||||
char *cp, *slash;
|
||||
struct timespec ts;
|
||||
struct iovec vec[3];
|
||||
|
||||
/* Make sure the logId value is not a bad idea */
|
||||
if ((logId == LOG_ID_KERNEL) || /* Verbotten */
|
||||
(logId == LOG_ID_EVENTS) || /* Do not support binary content */
|
||||
(logId == LOG_ID_SECURITY) || /* Bad idea to allow */
|
||||
((unsigned)logId >= 32)) { /* fit within logMask on arch32 */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
|
||||
cp = strdup(filename);
|
||||
if (!cp) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tag = cp;
|
||||
slash = strrchr(cp, '/');
|
||||
if (slash) {
|
||||
*slash = ':';
|
||||
slash = strrchr(cp, '/');
|
||||
if (slash) {
|
||||
tag = slash + 1;
|
||||
}
|
||||
}
|
||||
|
||||
length = strlen(tag) + 1;
|
||||
packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
|
||||
|
||||
vec[0].iov_base = &prio;
|
||||
vec[0].iov_len = sizeof(char);
|
||||
vec[1].iov_base = (unsigned char*)tag;
|
||||
vec[1].iov_len = length;
|
||||
|
||||
for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
|
||||
ssize_t ret;
|
||||
size_t transfer;
|
||||
|
||||
if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
|
||||
len -= length;
|
||||
break;
|
||||
}
|
||||
|
||||
transfer = length;
|
||||
if (transfer > packet_len) {
|
||||
transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
|
||||
if ((transfer < length) && (buf[transfer] == '\n')) {
|
||||
++transfer;
|
||||
}
|
||||
}
|
||||
|
||||
vec[2].iov_base = (unsigned char*)buf;
|
||||
vec[2].iov_len = transfer;
|
||||
|
||||
ret = PmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
|
||||
|
||||
if (ret <= 0) {
|
||||
free(cp);
|
||||
return ret ? ret : (len - length);
|
||||
}
|
||||
length -= transfer;
|
||||
buf += transfer;
|
||||
}
|
||||
free(cp);
|
||||
return len;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
|
||||
void PmsgClose();
|
|
@ -1,385 +0,0 @@
|
|||
/*
|
||||
** Copyright 2014, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include <log/log_properties.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "logger_write.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||
#include <sys/_system_properties.h>
|
||||
|
||||
static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static int lock() {
|
||||
/*
|
||||
* If we trigger a signal handler in the middle of locked activity and the
|
||||
* signal handler logs a message, we could get into a deadlock state.
|
||||
*/
|
||||
/*
|
||||
* Any contention, and we can turn around and use the non-cached method
|
||||
* in less time than the system call associated with a mutex to deal with
|
||||
* the contention.
|
||||
*/
|
||||
return pthread_mutex_trylock(&lock_loggable);
|
||||
}
|
||||
|
||||
static void unlock() {
|
||||
pthread_mutex_unlock(&lock_loggable);
|
||||
}
|
||||
|
||||
struct cache {
|
||||
const prop_info* pinfo;
|
||||
uint32_t serial;
|
||||
};
|
||||
|
||||
struct cache_char {
|
||||
struct cache cache;
|
||||
unsigned char c;
|
||||
};
|
||||
|
||||
static int check_cache(struct cache* cache) {
|
||||
return cache->pinfo && __system_property_serial(cache->pinfo) != cache->serial;
|
||||
}
|
||||
|
||||
#define BOOLEAN_TRUE 0xFF
|
||||
#define BOOLEAN_FALSE 0xFE
|
||||
|
||||
static void refresh_cache(struct cache_char* cache, const char* key) {
|
||||
char buf[PROP_VALUE_MAX];
|
||||
|
||||
if (!cache->cache.pinfo) {
|
||||
cache->cache.pinfo = __system_property_find(key);
|
||||
if (!cache->cache.pinfo) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
cache->cache.serial = __system_property_serial(cache->cache.pinfo);
|
||||
__system_property_read(cache->cache.pinfo, 0, buf);
|
||||
switch (buf[0]) {
|
||||
case 't':
|
||||
case 'T':
|
||||
cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
cache->c = strcasecmp(buf + 1, "alse") ? buf[0] : BOOLEAN_FALSE;
|
||||
break;
|
||||
default:
|
||||
cache->c = buf[0];
|
||||
}
|
||||
}
|
||||
|
||||
static int __android_log_level(const char* tag, size_t len) {
|
||||
/* sizeof() is used on this array below */
|
||||
static const char log_namespace[] = "persist.log.tag.";
|
||||
static const size_t base_offset = 8; /* skip "persist." */
|
||||
|
||||
if (tag == nullptr || len == 0) {
|
||||
auto& tag_string = GetDefaultTag();
|
||||
tag = tag_string.c_str();
|
||||
len = tag_string.size();
|
||||
}
|
||||
|
||||
/* sizeof(log_namespace) = strlen(log_namespace) + 1 */
|
||||
char key[sizeof(log_namespace) + len];
|
||||
char* kp;
|
||||
size_t i;
|
||||
char c = 0;
|
||||
/*
|
||||
* Single layer cache of four properties. Priorities are:
|
||||
* log.tag.<tag>
|
||||
* persist.log.tag.<tag>
|
||||
* log.tag
|
||||
* persist.log.tag
|
||||
* Where the missing tag matches all tags and becomes the
|
||||
* system global default. We do not support ro.log.tag* .
|
||||
*/
|
||||
static char* last_tag;
|
||||
static size_t last_tag_len;
|
||||
static uint32_t global_serial;
|
||||
/* some compilers erroneously see uninitialized use. !not_locked */
|
||||
uint32_t current_global_serial = 0;
|
||||
static struct cache_char tag_cache[2];
|
||||
static struct cache_char global_cache[2];
|
||||
int change_detected;
|
||||
int global_change_detected;
|
||||
int not_locked;
|
||||
|
||||
strcpy(key, log_namespace);
|
||||
|
||||
global_change_detected = change_detected = not_locked = lock();
|
||||
|
||||
if (!not_locked) {
|
||||
/*
|
||||
* check all known serial numbers to changes.
|
||||
*/
|
||||
for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
|
||||
if (check_cache(&tag_cache[i].cache)) {
|
||||
change_detected = 1;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
|
||||
if (check_cache(&global_cache[i].cache)) {
|
||||
global_change_detected = 1;
|
||||
}
|
||||
}
|
||||
|
||||
current_global_serial = __system_property_area_serial();
|
||||
if (current_global_serial != global_serial) {
|
||||
change_detected = 1;
|
||||
global_change_detected = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (len) {
|
||||
int local_change_detected = change_detected;
|
||||
if (!not_locked) {
|
||||
if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
|
||||
strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {
|
||||
/* invalidate log.tag.<tag> cache */
|
||||
for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
|
||||
tag_cache[i].cache.pinfo = NULL;
|
||||
tag_cache[i].c = '\0';
|
||||
}
|
||||
if (last_tag) last_tag[0] = '\0';
|
||||
local_change_detected = 1;
|
||||
}
|
||||
if (!last_tag || !last_tag[0]) {
|
||||
if (!last_tag) {
|
||||
last_tag = static_cast<char*>(calloc(1, len + 1));
|
||||
last_tag_len = 0;
|
||||
if (last_tag) last_tag_len = len + 1;
|
||||
} else if (len >= last_tag_len) {
|
||||
last_tag = static_cast<char*>(realloc(last_tag, len + 1));
|
||||
last_tag_len = 0;
|
||||
if (last_tag) last_tag_len = len + 1;
|
||||
}
|
||||
if (last_tag) {
|
||||
strncpy(last_tag, tag, len);
|
||||
last_tag[len] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
strncpy(key + sizeof(log_namespace) - 1, tag, len);
|
||||
key[sizeof(log_namespace) - 1 + len] = '\0';
|
||||
|
||||
kp = key;
|
||||
for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
|
||||
struct cache_char* cache = &tag_cache[i];
|
||||
struct cache_char temp_cache;
|
||||
|
||||
if (not_locked) {
|
||||
temp_cache.cache.pinfo = NULL;
|
||||
temp_cache.c = '\0';
|
||||
cache = &temp_cache;
|
||||
}
|
||||
if (local_change_detected) {
|
||||
refresh_cache(cache, kp);
|
||||
}
|
||||
|
||||
if (cache->c) {
|
||||
c = cache->c;
|
||||
break;
|
||||
}
|
||||
|
||||
kp = key + base_offset;
|
||||
}
|
||||
}
|
||||
|
||||
switch (toupper(c)) { /* if invalid, resort to global */
|
||||
case 'V':
|
||||
case 'D':
|
||||
case 'I':
|
||||
case 'W':
|
||||
case 'E':
|
||||
case 'F': /* Not officially supported */
|
||||
case 'A':
|
||||
case 'S':
|
||||
case BOOLEAN_FALSE: /* Not officially supported */
|
||||
break;
|
||||
default:
|
||||
/* clear '.' after log.tag */
|
||||
key[sizeof(log_namespace) - 2] = '\0';
|
||||
|
||||
kp = key;
|
||||
for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
|
||||
struct cache_char* cache = &global_cache[i];
|
||||
struct cache_char temp_cache;
|
||||
|
||||
if (not_locked) {
|
||||
temp_cache = *cache;
|
||||
if (temp_cache.cache.pinfo != cache->cache.pinfo) { /* check atomic */
|
||||
temp_cache.cache.pinfo = NULL;
|
||||
temp_cache.c = '\0';
|
||||
}
|
||||
cache = &temp_cache;
|
||||
}
|
||||
if (global_change_detected) {
|
||||
refresh_cache(cache, kp);
|
||||
}
|
||||
|
||||
if (cache->c) {
|
||||
c = cache->c;
|
||||
break;
|
||||
}
|
||||
|
||||
kp = key + base_offset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!not_locked) {
|
||||
global_serial = current_global_serial;
|
||||
unlock();
|
||||
}
|
||||
|
||||
switch (toupper(c)) {
|
||||
/* clang-format off */
|
||||
case 'V': return ANDROID_LOG_VERBOSE;
|
||||
case 'D': return ANDROID_LOG_DEBUG;
|
||||
case 'I': return ANDROID_LOG_INFO;
|
||||
case 'W': return ANDROID_LOG_WARN;
|
||||
case 'E': return ANDROID_LOG_ERROR;
|
||||
case 'F': /* FALLTHRU */ /* Not officially supported */
|
||||
case 'A': return ANDROID_LOG_FATAL;
|
||||
case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
|
||||
case 'S': return ANDROID_LOG_SILENT;
|
||||
/* clang-format on */
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
|
||||
int minimum_log_priority = __android_log_get_minimum_priority();
|
||||
int property_log_level = __android_log_level(tag, len);
|
||||
|
||||
if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) {
|
||||
return prio >= std::min(property_log_level, minimum_log_priority);
|
||||
} else if (property_log_level >= 0) {
|
||||
return prio >= property_log_level;
|
||||
} else if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
|
||||
return prio >= minimum_log_priority;
|
||||
} else {
|
||||
return prio >= default_prio;
|
||||
}
|
||||
}
|
||||
|
||||
int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
|
||||
auto len = tag ? strlen(tag) : 0;
|
||||
return __android_log_is_loggable_len(prio, tag, len, default_prio);
|
||||
}
|
||||
|
||||
int __android_log_is_debuggable() {
|
||||
static int is_debuggable = [] {
|
||||
char value[PROP_VALUE_MAX] = {};
|
||||
return __system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1");
|
||||
}();
|
||||
|
||||
return is_debuggable;
|
||||
}
|
||||
|
||||
/*
|
||||
* For properties that are read often, but generally remain constant.
|
||||
* Since a change is rare, we will accept a trylock failure gracefully.
|
||||
* Use a separate lock from is_loggable to keep contention down b/25563384.
|
||||
*/
|
||||
struct cache2_char {
|
||||
pthread_mutex_t lock;
|
||||
uint32_t serial;
|
||||
const char* key_persist;
|
||||
struct cache_char cache_persist;
|
||||
const char* key_ro;
|
||||
struct cache_char cache_ro;
|
||||
unsigned char (*const evaluate)(const struct cache2_char* self);
|
||||
};
|
||||
|
||||
static inline unsigned char do_cache2_char(struct cache2_char* self) {
|
||||
uint32_t current_serial;
|
||||
int change_detected;
|
||||
unsigned char c;
|
||||
|
||||
if (pthread_mutex_trylock(&self->lock)) {
|
||||
/* We are willing to accept some race in this context */
|
||||
return self->evaluate(self);
|
||||
}
|
||||
|
||||
change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
|
||||
current_serial = __system_property_area_serial();
|
||||
if (current_serial != self->serial) {
|
||||
change_detected = 1;
|
||||
}
|
||||
if (change_detected) {
|
||||
refresh_cache(&self->cache_persist, self->key_persist);
|
||||
refresh_cache(&self->cache_ro, self->key_ro);
|
||||
self->serial = current_serial;
|
||||
}
|
||||
c = self->evaluate(self);
|
||||
|
||||
pthread_mutex_unlock(&self->lock);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Security state generally remains constant, but the DO must be able
|
||||
* to turn off logging should it become spammy after an attack is detected.
|
||||
*/
|
||||
static unsigned char evaluate_security(const struct cache2_char* self) {
|
||||
unsigned char c = self->cache_ro.c;
|
||||
|
||||
return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
|
||||
}
|
||||
|
||||
int __android_log_security() {
|
||||
static struct cache2_char security = {
|
||||
PTHREAD_MUTEX_INITIALIZER, 0,
|
||||
"persist.logd.security", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
|
||||
"ro.organization_owned", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
|
||||
evaluate_security};
|
||||
|
||||
return do_cache2_char(&security);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int __android_log_is_loggable(int prio, const char*, int) {
|
||||
int minimum_priority = __android_log_get_minimum_priority();
|
||||
if (minimum_priority == ANDROID_LOG_DEFAULT) {
|
||||
minimum_priority = ANDROID_LOG_INFO;
|
||||
}
|
||||
return prio >= minimum_priority;
|
||||
}
|
||||
|
||||
int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
|
||||
return __android_log_is_loggable(prio, nullptr, def);
|
||||
}
|
||||
|
||||
int __android_log_is_debuggable() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,114 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2013-2014 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Benchmarks.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Build benchmarks for the device. Run with:
|
||||
// adb shell liblog-benchmarks
|
||||
cc_benchmark {
|
||||
name: "liblog-benchmarks",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fno-builtin",
|
||||
],
|
||||
shared_libs: [
|
||||
"libm",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
],
|
||||
static_libs: ["liblog"],
|
||||
srcs: ["liblog_benchmark.cpp"],
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Unit tests.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
cc_defaults {
|
||||
name: "liblog-tests-defaults",
|
||||
|
||||
cflags: [
|
||||
"-fstack-protector-all",
|
||||
"-g",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fno-builtin",
|
||||
],
|
||||
srcs: [
|
||||
"libc_test.cpp",
|
||||
"liblog_default_tag.cpp",
|
||||
"liblog_global_state.cpp",
|
||||
"liblog_test.cpp",
|
||||
"log_id_test.cpp",
|
||||
"log_radio_test.cpp",
|
||||
"log_read_test.cpp",
|
||||
"log_system_test.cpp",
|
||||
"log_time_test.cpp",
|
||||
"log_wrap_test.cpp",
|
||||
"logd_writer_test.cpp",
|
||||
"logprint_test.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
"libbase",
|
||||
],
|
||||
static_libs: ["liblog"],
|
||||
isolated: true,
|
||||
require_root: true,
|
||||
}
|
||||
|
||||
// Build tests for the device (with .so). Run with:
|
||||
// adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
|
||||
cc_test {
|
||||
name: "liblog-unit-tests",
|
||||
defaults: ["liblog-tests-defaults"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "CtsLiblogTestCases",
|
||||
defaults: ["liblog-tests-defaults"],
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
|
||||
cflags: ["-DNO_PSTORE"],
|
||||
test_suites: [
|
||||
"cts",
|
||||
"device-tests",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test_host {
|
||||
name: "liblog-host-test",
|
||||
static_libs: ["liblog"],
|
||||
shared_libs: ["libbase"],
|
||||
srcs: [
|
||||
"liblog_host_test.cpp",
|
||||
"liblog_default_tag.cpp",
|
||||
"liblog_global_state.cpp",
|
||||
],
|
||||
isolated: true,
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<configuration description="Config for CTS Logging Library test cases">
|
||||
<option name="test-suite-tag" value="cts" />
|
||||
<option name="config-descriptor:metadata" key="component" value="systems" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
|
||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
|
||||
<option name="append-bitness" value="true" />
|
||||
</target_preparer>
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="native-test-device-path" value="/data/local/tmp" />
|
||||
<option name="module-name" value="CtsLiblogTestCases" />
|
||||
<option name="runtime-hint" value="65s" />
|
||||
</test>
|
||||
</configuration>
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
TEST(libc, __pstore_append) {
|
||||
#ifdef __ANDROID__
|
||||
#ifndef NO_PSTORE
|
||||
if (access("/dev/pmsg0", W_OK) != 0) {
|
||||
GTEST_SKIP() << "pmsg0 not found, skipping test";
|
||||
}
|
||||
|
||||
FILE* fp;
|
||||
ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "ae")));
|
||||
static const char message[] = "libc.__pstore_append\n";
|
||||
ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
|
||||
int fflushReturn = fflush(fp);
|
||||
int fflushErrno = fflushReturn ? errno : 0;
|
||||
ASSERT_EQ(0, fflushReturn);
|
||||
ASSERT_EQ(0, fflushErrno);
|
||||
int fcloseReturn = fclose(fp);
|
||||
int fcloseErrno = fcloseReturn ? errno : 0;
|
||||
ASSERT_EQ(0, fcloseReturn);
|
||||
ASSERT_EQ(0, fcloseErrno);
|
||||
if ((fcloseErrno == ENOMEM) || (fflushErrno == ENOMEM)) {
|
||||
fprintf(stderr,
|
||||
"Kernel does not have space allocated to pmsg pstore driver "
|
||||
"configured\n");
|
||||
}
|
||||
if (!fcloseReturn && !fcloseErrno && !fflushReturn && !fflushReturn) {
|
||||
fprintf(stderr,
|
||||
"Reboot, ensure string libc.__pstore_append is in "
|
||||
"/sys/fs/pstore/pmsg-ramoops-0\n");
|
||||
}
|
||||
#else /* NO_PSTORE */
|
||||
GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
|
||||
#endif /* NO_PSTORE */
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// LOG_TAG must be unset for android-base's logging to use a default tag.
|
||||
#undef LOG_TAG
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/scopeguard.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifndef __ANDROID__
|
||||
static const char* getprogname() {
|
||||
return program_invocation_short_name;
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(liblog_default_tag, no_default_tag_libbase_write_first) {
|
||||
using namespace android::base;
|
||||
bool message_seen = false;
|
||||
std::string expected_tag = "";
|
||||
SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(expected_tag, tag);
|
||||
});
|
||||
|
||||
expected_tag = getprogname();
|
||||
LOG(WARNING) << "message";
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
|
||||
__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
|
||||
EXPECT_TRUE(message_seen);
|
||||
}
|
||||
|
||||
TEST(liblog_default_tag, no_default_tag_liblog_write_first) {
|
||||
using namespace android::base;
|
||||
bool message_seen = false;
|
||||
std::string expected_tag = "";
|
||||
SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(expected_tag, tag);
|
||||
});
|
||||
|
||||
expected_tag = getprogname();
|
||||
__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
|
||||
LOG(WARNING) << "message";
|
||||
EXPECT_TRUE(message_seen);
|
||||
}
|
||||
|
||||
TEST(liblog_default_tag, libbase_sets_default_tag) {
|
||||
using namespace android::base;
|
||||
bool message_seen = false;
|
||||
std::string expected_tag = "libbase_test_tag";
|
||||
SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(expected_tag, tag);
|
||||
});
|
||||
SetDefaultTag(expected_tag);
|
||||
|
||||
__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
|
||||
LOG(WARNING) << "message";
|
||||
EXPECT_TRUE(message_seen);
|
||||
}
|
||||
|
||||
TEST(liblog_default_tag, liblog_sets_default_tag) {
|
||||
using namespace android::base;
|
||||
bool message_seen = false;
|
||||
std::string expected_tag = "liblog_test_tag";
|
||||
SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(expected_tag, tag);
|
||||
});
|
||||
__android_log_set_default_tag(expected_tag.c_str());
|
||||
|
||||
__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
|
||||
LOG(WARNING) << "message";
|
||||
EXPECT_TRUE(message_seen);
|
||||
}
|
||||
|
||||
TEST(liblog_default_tag, default_tag_plus_log_severity) {
|
||||
#ifdef __ANDROID__
|
||||
using namespace android::base;
|
||||
bool message_seen = false;
|
||||
std::string expected_tag = "liblog_test_tag";
|
||||
SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(expected_tag, tag);
|
||||
});
|
||||
__android_log_set_default_tag(expected_tag.c_str());
|
||||
|
||||
auto log_tag_property = "log.tag." + expected_tag;
|
||||
SetProperty(log_tag_property, "V");
|
||||
auto reset_tag_property_guard = make_scope_guard([=] { SetProperty(log_tag_property, ""); });
|
||||
|
||||
__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, nullptr, "message");
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
|
||||
LOG(VERBOSE) << "message";
|
||||
EXPECT_TRUE(message_seen);
|
||||
#else
|
||||
GTEST_SKIP() << "No log tag properties on host";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(liblog_default_tag, generated_default_tag_plus_log_severity) {
|
||||
#ifdef __ANDROID__
|
||||
using namespace android::base;
|
||||
bool message_seen = false;
|
||||
std::string expected_tag = getprogname();
|
||||
SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(expected_tag, tag);
|
||||
});
|
||||
|
||||
// Even without any calls to SetDefaultTag(), the first message that attempts to log, will
|
||||
// generate a default tag from getprogname() and check log.tag.<default tag> for loggability. This
|
||||
// case checks that we can log a Verbose message when log.tag.<getprogname()> is set to 'V'.
|
||||
auto log_tag_property = "log.tag." + expected_tag;
|
||||
SetProperty(log_tag_property, "V");
|
||||
auto reset_tag_property_guard = make_scope_guard([=] { SetProperty(log_tag_property, ""); });
|
||||
|
||||
__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, nullptr, "message");
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
|
||||
LOG(VERBOSE) << "message";
|
||||
EXPECT_TRUE(message_seen);
|
||||
#else
|
||||
GTEST_SKIP() << "No log tag properties on host";
|
||||
#endif
|
||||
}
|
|
@ -1,259 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "global_state_test_tag"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(liblog_global_state, libbase_logs_with_libbase_SetLogger) {
|
||||
using namespace android::base;
|
||||
bool message_seen = false;
|
||||
LogSeverity expected_severity = WARNING;
|
||||
std::string expected_file = Basename(__FILE__);
|
||||
unsigned int expected_line;
|
||||
std::string expected_message = "libbase test message";
|
||||
|
||||
auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
|
||||
unsigned int line, const char* message) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(DEFAULT, log_id);
|
||||
EXPECT_EQ(expected_severity, severity);
|
||||
EXPECT_STREQ(LOG_TAG, tag);
|
||||
EXPECT_EQ(expected_file, file);
|
||||
EXPECT_EQ(expected_line, line);
|
||||
EXPECT_EQ(expected_message, message);
|
||||
};
|
||||
|
||||
SetLogger(LoggerFunction);
|
||||
|
||||
expected_line = __LINE__ + 1;
|
||||
LOG(expected_severity) << expected_message;
|
||||
EXPECT_TRUE(message_seen);
|
||||
}
|
||||
|
||||
TEST(liblog_global_state, libbase_logs_with_liblog_set_logger) {
|
||||
using namespace android::base;
|
||||
// These must be static since they're used by the liblog logger function, which only accepts
|
||||
// lambdas without captures. The items used by the libbase logger are explicitly not static, to
|
||||
// ensure that lambdas with captures do work there.
|
||||
static bool message_seen = false;
|
||||
static std::string expected_file = Basename(__FILE__);
|
||||
static unsigned int expected_line;
|
||||
static std::string expected_message = "libbase test message";
|
||||
|
||||
auto liblog_logger_function = [](const struct __android_log_message* log_message) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(sizeof(__android_log_message), log_message->struct_size);
|
||||
EXPECT_EQ(LOG_ID_DEFAULT, log_message->buffer_id);
|
||||
EXPECT_EQ(ANDROID_LOG_WARN, log_message->priority);
|
||||
EXPECT_STREQ(LOG_TAG, log_message->tag);
|
||||
EXPECT_EQ(expected_file, log_message->file);
|
||||
EXPECT_EQ(expected_line, log_message->line);
|
||||
EXPECT_EQ(expected_message, log_message->message);
|
||||
};
|
||||
|
||||
__android_log_set_logger(liblog_logger_function);
|
||||
|
||||
expected_line = __LINE__ + 1;
|
||||
LOG(WARNING) << expected_message;
|
||||
EXPECT_TRUE(message_seen);
|
||||
}
|
||||
|
||||
TEST(liblog_global_state, liblog_logs_with_libbase_SetLogger) {
|
||||
using namespace android::base;
|
||||
bool message_seen = false;
|
||||
std::string expected_message = "libbase test message";
|
||||
|
||||
auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
|
||||
unsigned int line, const char* message) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(MAIN, log_id);
|
||||
EXPECT_EQ(WARNING, severity);
|
||||
EXPECT_STREQ(LOG_TAG, tag);
|
||||
EXPECT_EQ(nullptr, file);
|
||||
EXPECT_EQ(0U, line);
|
||||
EXPECT_EQ(expected_message, message);
|
||||
};
|
||||
|
||||
SetLogger(LoggerFunction);
|
||||
|
||||
__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, LOG_TAG, expected_message.c_str());
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
}
|
||||
|
||||
TEST(liblog_global_state, liblog_logs_with_liblog_set_logger) {
|
||||
using namespace android::base;
|
||||
// These must be static since they're used by the liblog logger function, which only accepts
|
||||
// lambdas without captures. The items used by the libbase logger are explicitly not static, to
|
||||
// ensure that lambdas with captures do work there.
|
||||
static bool message_seen = false;
|
||||
static int expected_buffer_id = LOG_ID_MAIN;
|
||||
static int expected_priority = ANDROID_LOG_WARN;
|
||||
static std::string expected_message = "libbase test message";
|
||||
|
||||
auto liblog_logger_function = [](const struct __android_log_message* log_message) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(sizeof(__android_log_message), log_message->struct_size);
|
||||
EXPECT_EQ(expected_buffer_id, log_message->buffer_id);
|
||||
EXPECT_EQ(expected_priority, log_message->priority);
|
||||
EXPECT_STREQ(LOG_TAG, log_message->tag);
|
||||
EXPECT_STREQ(nullptr, log_message->file);
|
||||
EXPECT_EQ(0U, log_message->line);
|
||||
EXPECT_EQ(expected_message, log_message->message);
|
||||
};
|
||||
|
||||
__android_log_set_logger(liblog_logger_function);
|
||||
|
||||
__android_log_buf_write(expected_buffer_id, expected_priority, LOG_TAG, expected_message.c_str());
|
||||
EXPECT_TRUE(message_seen);
|
||||
}
|
||||
|
||||
TEST(liblog_global_state, SetAborter_with_liblog) {
|
||||
using namespace android::base;
|
||||
|
||||
std::string expected_message = "libbase test message";
|
||||
static bool message_seen = false;
|
||||
auto aborter_function = [&](const char* message) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(expected_message, message);
|
||||
};
|
||||
|
||||
SetAborter(aborter_function);
|
||||
LOG(FATAL) << expected_message;
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
|
||||
static std::string expected_message_static = "libbase test message";
|
||||
auto liblog_aborter_function = [](const char* message) {
|
||||
message_seen = true;
|
||||
EXPECT_EQ(expected_message_static, message);
|
||||
};
|
||||
__android_log_set_aborter(liblog_aborter_function);
|
||||
LOG(FATAL) << expected_message_static;
|
||||
EXPECT_TRUE(message_seen);
|
||||
message_seen = false;
|
||||
}
|
||||
|
||||
static std::string UniqueLogTag() {
|
||||
std::string tag = LOG_TAG;
|
||||
tag += "-" + std::to_string(getpid());
|
||||
return tag;
|
||||
}
|
||||
|
||||
TEST(liblog_global_state, is_loggable_both_default) {
|
||||
auto tag = UniqueLogTag();
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
}
|
||||
|
||||
TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
|
||||
auto tag = UniqueLogTag();
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
}
|
||||
|
||||
TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
|
||||
#ifdef __ANDROID__
|
||||
auto tag = UniqueLogTag();
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
auto log_tag_property = std::string("log.tag.") + tag;
|
||||
ASSERT_TRUE(android::base::SetProperty(log_tag_property, "d"));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
ASSERT_TRUE(android::base::SetProperty(log_tag_property, "w"));
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
ASSERT_TRUE(android::base::SetProperty(log_tag_property, ""));
|
||||
#else
|
||||
GTEST_SKIP() << "No log tag properties on host";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(liblog_global_state, is_loggable_both_set) {
|
||||
#ifdef __ANDROID__
|
||||
auto tag = UniqueLogTag();
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
// When both a tag and a minimum priority are set, we use the lower value of the two.
|
||||
|
||||
// tag = warning, minimum_priority = debug, expect 'debug'
|
||||
auto log_tag_property = std::string("log.tag.") + tag;
|
||||
ASSERT_TRUE(android::base::SetProperty(log_tag_property, "w"));
|
||||
EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
// tag = warning, minimum_priority = warning, expect 'warning'
|
||||
EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
// tag = debug, minimum_priority = warning, expect 'debug'
|
||||
ASSERT_TRUE(android::base::SetProperty(log_tag_property, "d"));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
// tag = debug, minimum_priority = debug, expect 'debug'
|
||||
EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
|
||||
EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
|
||||
|
||||
ASSERT_TRUE(android::base::SetProperty(log_tag_property, ""));
|
||||
#else
|
||||
GTEST_SKIP() << "No log tag properties on host";
|
||||
#endif
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <log/log.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/test_utils.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using android::base::InitLogging;
|
||||
using android::base::StderrLogger;
|
||||
using android::base::StringPrintf;
|
||||
|
||||
static std::string MakeLogPattern(int priority, const char* tag, const char* message) {
|
||||
static const char log_characters[] = "XXVDIWEF";
|
||||
static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
|
||||
"Mismatch in size of log_characters and values in android_LogPriority");
|
||||
priority = priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : priority;
|
||||
char log_char = log_characters[priority];
|
||||
|
||||
return StringPrintf("%s %c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s", tag, log_char,
|
||||
message);
|
||||
}
|
||||
|
||||
static void CheckMessage(bool expected, const std::string& output, int priority, const char* tag,
|
||||
const char* message) {
|
||||
std::regex message_regex(MakeLogPattern(priority, tag, message));
|
||||
EXPECT_EQ(expected, std::regex_search(output, message_regex)) << message;
|
||||
}
|
||||
|
||||
static void GenerateLogContent() {
|
||||
__android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, "tag", "verbose main");
|
||||
__android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, "tag", "info main");
|
||||
__android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_ERROR, "tag", "error main");
|
||||
|
||||
__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, "tag", "verbose radio");
|
||||
__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, "tag", "info radio");
|
||||
__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, "tag", "error radio");
|
||||
|
||||
__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, "tag", "verbose system");
|
||||
__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, "tag", "info system");
|
||||
__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, "tag", "error system");
|
||||
|
||||
__android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_VERBOSE, "tag", "verbose crash");
|
||||
__android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_INFO, "tag", "info crash");
|
||||
__android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_ERROR, "tag", "error crash");
|
||||
}
|
||||
|
||||
std::string GetPidString() {
|
||||
int pid = getpid();
|
||||
return StringPrintf("%5d", pid);
|
||||
}
|
||||
|
||||
TEST(liblog, default_write) {
|
||||
CapturedStderr captured_stderr;
|
||||
InitLogging(nullptr, StderrLogger);
|
||||
|
||||
GenerateLogContent();
|
||||
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
|
||||
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
|
||||
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
|
||||
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
|
||||
}
|
||||
|
||||
TEST(liblog, verbose_write) {
|
||||
setenv("ANDROID_LOG_TAGS", "*:v", true);
|
||||
CapturedStderr captured_stderr;
|
||||
InitLogging(nullptr, StderrLogger);
|
||||
|
||||
GenerateLogContent();
|
||||
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
|
||||
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
|
||||
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
|
||||
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
|
||||
}
|
||||
|
||||
TEST(liblog, error_write) {
|
||||
setenv("ANDROID_LOG_TAGS", "*:e", true);
|
||||
CapturedStderr captured_stderr;
|
||||
InitLogging(nullptr, StderrLogger);
|
||||
|
||||
GenerateLogContent();
|
||||
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
|
||||
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
|
||||
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
|
||||
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
|
||||
CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
|
||||
CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
|
||||
}
|
||||
|
||||
TEST(liblog, kernel_no_write) {
|
||||
CapturedStderr captured_stderr;
|
||||
InitLogging(nullptr, StderrLogger);
|
||||
__android_log_buf_print(LOG_ID_KERNEL, ANDROID_LOG_ERROR, "tag", "kernel error");
|
||||
EXPECT_EQ("", captured_stderr.str());
|
||||
}
|
||||
|
||||
TEST(liblog, binary_no_write) {
|
||||
CapturedStderr captured_stderr;
|
||||
InitLogging(nullptr, StderrLogger);
|
||||
__android_log_buf_print(LOG_ID_EVENTS, ANDROID_LOG_ERROR, "tag", "error events");
|
||||
__android_log_buf_print(LOG_ID_STATS, ANDROID_LOG_ERROR, "tag", "error stats");
|
||||
__android_log_buf_print(LOG_ID_SECURITY, ANDROID_LOG_ERROR, "tag", "error security");
|
||||
|
||||
__android_log_bswrite(0x12, "events");
|
||||
__android_log_stats_bwrite(0x34, "stats", strlen("stats"));
|
||||
__android_log_security_bswrite(0x56, "security");
|
||||
|
||||
EXPECT_EQ("", captured_stderr.str());
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
// Test the APIs in this standalone include file
|
||||
#include <log/log_id.h>
|
||||
|
||||
// We do not want to include <android/log.h> to acquire ANDROID_LOG_INFO for
|
||||
// include file API purity. We do however want to allow the _option_ that
|
||||
// log/log_id.h could include this file, or related content, in the future.
|
||||
#ifndef __android_LogPriority_defined
|
||||
#define ANDROID_LOG_INFO 4
|
||||
#endif
|
||||
|
||||
TEST(liblog, log_id) {
|
||||
int count = 0;
|
||||
|
||||
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
|
||||
log_id_t id = static_cast<log_id_t>(i);
|
||||
const char* name = android_log_id_to_name(id);
|
||||
if (id != android_name_to_log_id(name)) {
|
||||
continue;
|
||||
}
|
||||
++count;
|
||||
fprintf(stderr, "log buffer %s\r", name);
|
||||
}
|
||||
ASSERT_EQ(LOG_ID_MAX, count);
|
||||
}
|
||||
|
||||
TEST(liblog, __android_log_buf_print) {
|
||||
EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
|
||||
"TEST__android_log_buf_print", "radio"));
|
||||
usleep(1000);
|
||||
EXPECT_LT(0,
|
||||
__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
|
||||
"TEST__android_log_buf_print", "system"));
|
||||
usleep(1000);
|
||||
EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
|
||||
"TEST__android_log_buf_print", "main"));
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
TEST(liblog, __android_log_buf_write) {
|
||||
EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
|
||||
"TEST__android_log_buf_write", "radio"));
|
||||
usleep(1000);
|
||||
EXPECT_LT(0,
|
||||
__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
|
||||
"TEST__android_log_buf_write", "system"));
|
||||
usleep(1000);
|
||||
EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
|
||||
"TEST__android_log_buf_write", "main"));
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
static void* ConcurrentPrintFn(void* arg) {
|
||||
int ret = __android_log_buf_print(
|
||||
LOG_ID_MAIN, ANDROID_LOG_INFO, "TEST__android_log_print",
|
||||
"Concurrent %" PRIuPTR, reinterpret_cast<uintptr_t>(arg));
|
||||
return reinterpret_cast<void*>(ret);
|
||||
}
|
||||
|
||||
#define NUM_CONCURRENT 64
|
||||
#define _concurrent_name(a, n) a##__concurrent##n
|
||||
#define concurrent_name(a, n) _concurrent_name(a, n)
|
||||
|
||||
TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) {
|
||||
pthread_t t[NUM_CONCURRENT];
|
||||
int i;
|
||||
for (i = 0; i < NUM_CONCURRENT; i++) {
|
||||
ASSERT_EQ(0, pthread_create(&t[i], NULL, ConcurrentPrintFn,
|
||||
reinterpret_cast<void*>(i)));
|
||||
}
|
||||
int ret = 1;
|
||||
for (i = 0; i < NUM_CONCURRENT; i++) {
|
||||
void* result;
|
||||
ASSERT_EQ(0, pthread_join(t[i], &result));
|
||||
int this_result = reinterpret_cast<uintptr_t>(result);
|
||||
if ((0 < ret) && (ret != this_result)) {
|
||||
ret = this_result;
|
||||
}
|
||||
}
|
||||
ASSERT_LT(0, ret);
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <gtest/gtest.h>
|
||||
// Test the APIs in this standalone include file
|
||||
#include <log/log_radio.h>
|
||||
|
||||
TEST(liblog, RLOG) {
|
||||
static const char content[] = "log_radio.h";
|
||||
static const char content_false[] = "log_radio.h false";
|
||||
|
||||
// ratelimit content to 10/s to keep away from spam filters
|
||||
// do not send identical content together to keep away from spam filters
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGV"
|
||||
RLOGV(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGD"
|
||||
RLOGD(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGI"
|
||||
RLOGI(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGW"
|
||||
RLOGW(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGE"
|
||||
RLOGE(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGV"
|
||||
RLOGV_IF(true, content);
|
||||
usleep(100000);
|
||||
RLOGV_IF(false, content_false);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGD"
|
||||
RLOGD_IF(true, content);
|
||||
usleep(100000);
|
||||
RLOGD_IF(false, content_false);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGI"
|
||||
RLOGI_IF(true, content);
|
||||
usleep(100000);
|
||||
RLOGI_IF(false, content_false);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGW"
|
||||
RLOGW_IF(true, content);
|
||||
usleep(100000);
|
||||
RLOGW_IF(false, content_false);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__RLOGE"
|
||||
RLOGE_IF(true, content);
|
||||
usleep(100000);
|
||||
RLOGE_IF(false, content_false);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// give time for content to long-path through logger
|
||||
sleep(1);
|
||||
|
||||
std::string buf = android::base::StringPrintf(
|
||||
"logcat -b radio --pid=%u -d -s"
|
||||
" TEST__RLOGV TEST__RLOGD TEST__RLOGI TEST__RLOGW TEST__RLOGE",
|
||||
(unsigned)getpid());
|
||||
FILE* fp = popen(buf.c_str(), "re");
|
||||
int count = 0;
|
||||
int count_false = 0;
|
||||
if (fp) {
|
||||
if (!android::base::ReadFdToString(fileno(fp), &buf)) buf = "";
|
||||
pclose(fp);
|
||||
for (size_t pos = 0; (pos = buf.find(content, pos)) != std::string::npos;
|
||||
++pos) {
|
||||
++count;
|
||||
}
|
||||
for (size_t pos = 0;
|
||||
(pos = buf.find(content_false, pos)) != std::string::npos; ++pos) {
|
||||
++count_false;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(0, count_false);
|
||||
#if LOG_NDEBUG
|
||||
ASSERT_EQ(8, count);
|
||||
#else
|
||||
ASSERT_EQ(10, count);
|
||||
#endif
|
||||
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
|
||||
#endif
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android/log.h> // minimal logging API
|
||||
#include <gtest/gtest.h>
|
||||
#include <log/log_properties.h>
|
||||
// Test the APIs in this standalone include file
|
||||
#include <log/log_read.h>
|
||||
// Do not use anything in log/log_time.h despite side effects of the above.
|
||||
#include <private/android_logger.h>
|
||||
|
||||
using android::base::GetBoolProperty;
|
||||
|
||||
TEST(liblog, android_logger_get_) {
|
||||
#ifdef __ANDROID__
|
||||
// This test assumes the log buffers are filled with noise from
|
||||
// normal operations. It will fail if done immediately after a
|
||||
// logcat -c.
|
||||
struct logger_list* logger_list = android_logger_list_alloc(0, 0, 0);
|
||||
|
||||
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
|
||||
log_id_t id = static_cast<log_id_t>(i);
|
||||
std::string name = android_log_id_to_name(id);
|
||||
fprintf(stderr, "log buffer %s\r", name.c_str());
|
||||
struct logger* logger;
|
||||
EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
|
||||
EXPECT_EQ(id, android_logger_get_id(logger));
|
||||
ssize_t get_log_size = android_logger_get_log_size(logger);
|
||||
/* security buffer is allowed to be denied */
|
||||
if (name != "security") {
|
||||
EXPECT_GT(get_log_size, 0);
|
||||
// crash buffer is allowed to be empty, that is actually healthy!
|
||||
// stats buffer is no longer in use.
|
||||
if (name == "crash" || name == "stats") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// kernel buffer is empty if ro.logd.kernel is false
|
||||
if (name == "kernel" && !GetBoolProperty("ro.logd.kernel", false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EXPECT_LE(0, android_logger_get_log_readable_size(logger));
|
||||
} else {
|
||||
EXPECT_NE(0, get_log_size);
|
||||
if (get_log_size < 0) {
|
||||
EXPECT_GT(0, android_logger_get_log_readable_size(logger));
|
||||
} else {
|
||||
EXPECT_LE(0, android_logger_get_log_readable_size(logger));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android_logger_list_close(logger_list);
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <gtest/gtest.h>
|
||||
// Test the APIs in this standalone include file
|
||||
#include <log/log_system.h>
|
||||
|
||||
TEST(liblog, SLOG) {
|
||||
static const char content[] = "log_system.h";
|
||||
static const char content_false[] = "log_system.h false";
|
||||
|
||||
// ratelimit content to 10/s to keep away from spam filters
|
||||
// do not send identical content together to keep away from spam filters
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGV"
|
||||
SLOGV(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGD"
|
||||
SLOGD(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGI"
|
||||
SLOGI(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGW"
|
||||
SLOGW(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGE"
|
||||
SLOGE(content);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGV"
|
||||
SLOGV_IF(true, content);
|
||||
usleep(100000);
|
||||
SLOGV_IF(false, content_false);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGD"
|
||||
SLOGD_IF(true, content);
|
||||
usleep(100000);
|
||||
SLOGD_IF(false, content_false);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGI"
|
||||
SLOGI_IF(true, content);
|
||||
usleep(100000);
|
||||
SLOGI_IF(false, content_false);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGW"
|
||||
SLOGW_IF(true, content);
|
||||
usleep(100000);
|
||||
SLOGW_IF(false, content_false);
|
||||
usleep(100000);
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TEST__SLOGE"
|
||||
SLOGE_IF(true, content);
|
||||
usleep(100000);
|
||||
SLOGE_IF(false, content_false);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// give time for content to long-path through logger
|
||||
sleep(1);
|
||||
|
||||
std::string buf = android::base::StringPrintf(
|
||||
"logcat -b system --pid=%u -d -s"
|
||||
" TEST__SLOGV TEST__SLOGD TEST__SLOGI TEST__SLOGW TEST__SLOGE",
|
||||
(unsigned)getpid());
|
||||
FILE* fp = popen(buf.c_str(), "re");
|
||||
int count = 0;
|
||||
int count_false = 0;
|
||||
if (fp) {
|
||||
if (!android::base::ReadFdToString(fileno(fp), &buf)) buf = "";
|
||||
pclose(fp);
|
||||
for (size_t pos = 0; (pos = buf.find(content, pos)) != std::string::npos;
|
||||
++pos) {
|
||||
++count;
|
||||
}
|
||||
for (size_t pos = 0;
|
||||
(pos = buf.find(content_false, pos)) != std::string::npos; ++pos) {
|
||||
++count_false;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(0, count_false);
|
||||
#if LOG_NDEBUG
|
||||
ASSERT_EQ(8, count);
|
||||
#else
|
||||
ASSERT_EQ(10, count);
|
||||
#endif
|
||||
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
|
||||
#endif
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
// Test the APIs in this standalone include file
|
||||
#include <log/log_time.h>
|
||||
|
||||
TEST(liblog, log_time) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
log_time tl(ts);
|
||||
|
||||
EXPECT_EQ(tl, ts);
|
||||
EXPECT_GE(tl, ts);
|
||||
EXPECT_LE(tl, ts);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android/log.h> // minimal logging API
|
||||
#include <gtest/gtest.h>
|
||||
#include <log/log_properties.h>
|
||||
#include <log/log_read.h>
|
||||
#include <log/log_time.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static void read_with_wrap() {
|
||||
// Read the last line in the log to get a starting timestamp. We're assuming
|
||||
// the log is not empty.
|
||||
const int mode = ANDROID_LOG_NONBLOCK;
|
||||
struct logger_list* logger_list =
|
||||
android_logger_list_open(LOG_ID_MAIN, mode, 1000, 0);
|
||||
|
||||
ASSERT_NE(logger_list, nullptr);
|
||||
|
||||
log_msg log_msg;
|
||||
int ret = android_logger_list_read(logger_list, &log_msg);
|
||||
android_logger_list_close(logger_list);
|
||||
ASSERT_GT(ret, 0);
|
||||
|
||||
log_time start(log_msg.entry.sec, log_msg.entry.nsec);
|
||||
ASSERT_NE(start, log_time());
|
||||
|
||||
logger_list =
|
||||
android_logger_list_alloc_time(mode | ANDROID_LOG_WRAP, start, 0);
|
||||
ASSERT_NE(logger_list, nullptr);
|
||||
|
||||
struct logger* logger = android_logger_open(logger_list, LOG_ID_MAIN);
|
||||
EXPECT_NE(logger, nullptr);
|
||||
if (logger) {
|
||||
android_logger_list_read(logger_list, &log_msg);
|
||||
}
|
||||
|
||||
android_logger_list_close(logger_list);
|
||||
}
|
||||
#endif
|
||||
|
||||
// b/64143705 confirm fixed
|
||||
TEST(liblog, wrap_mode_blocks) {
|
||||
#ifdef __ANDROID__
|
||||
// The read call is expected to take up to 2 hours in the happy case. There was a previous bug
|
||||
// where it would take only 30 seconds due to an alarm() in logd_reader.cpp. That alarm has been
|
||||
// removed, so we check here that the read call blocks for a reasonable amount of time (5s).
|
||||
|
||||
struct sigaction ignore = {.sa_handler = [](int) { _exit(0); }};
|
||||
struct sigaction old_sigaction;
|
||||
sigaction(SIGALRM, &ignore, &old_sigaction);
|
||||
alarm(5);
|
||||
|
||||
android::base::Timer timer;
|
||||
read_with_wrap();
|
||||
|
||||
FAIL() << "read_with_wrap() should not return before the alarm is triggered.";
|
||||
|
||||
alarm(0);
|
||||
sigaction(SIGALRM, &old_sigaction, nullptr);
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using android::base::StringPrintf;
|
||||
using android::base::unique_fd;
|
||||
|
||||
// logd_writer takes advantage of the fact that connect() can be called multiple times for a DGRAM
|
||||
// socket. This tests for that behavior.
|
||||
TEST(liblog, multi_connect_dgram_socket) {
|
||||
#ifdef __ANDROID__
|
||||
if (getuid() != 0) {
|
||||
GTEST_SKIP() << "Skipping test, must be run as root.";
|
||||
return;
|
||||
}
|
||||
auto temp_dir = TemporaryDir();
|
||||
auto socket_path = StringPrintf("%s/test_socket", temp_dir.path);
|
||||
|
||||
unique_fd server_socket;
|
||||
|
||||
auto open_server_socket = [&] {
|
||||
server_socket.reset(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
|
||||
ASSERT_TRUE(server_socket.ok());
|
||||
|
||||
sockaddr_un server_sockaddr = {};
|
||||
server_sockaddr.sun_family = AF_UNIX;
|
||||
strlcpy(server_sockaddr.sun_path, socket_path.c_str(), sizeof(server_sockaddr.sun_path));
|
||||
ASSERT_EQ(0,
|
||||
TEMP_FAILURE_RETRY(bind(server_socket, reinterpret_cast<sockaddr*>(&server_sockaddr),
|
||||
sizeof(server_sockaddr))));
|
||||
};
|
||||
|
||||
// Open the server socket.
|
||||
open_server_socket();
|
||||
|
||||
// Open the client socket.
|
||||
auto client_socket =
|
||||
unique_fd{TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0))};
|
||||
ASSERT_TRUE(client_socket.ok());
|
||||
sockaddr_un client_sockaddr = {};
|
||||
client_sockaddr.sun_family = AF_UNIX;
|
||||
strlcpy(client_sockaddr.sun_path, socket_path.c_str(), sizeof(client_sockaddr.sun_path));
|
||||
ASSERT_EQ(0,
|
||||
TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
|
||||
sizeof(client_sockaddr))));
|
||||
|
||||
// Ensure that communication works.
|
||||
constexpr static char kSmoke[] = "smoke test";
|
||||
ssize_t smoke_len = sizeof(kSmoke);
|
||||
ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
|
||||
char read_buf[512];
|
||||
ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
|
||||
ASSERT_STREQ(kSmoke, read_buf);
|
||||
|
||||
// Close the server socket.
|
||||
server_socket.reset();
|
||||
ASSERT_EQ(0, unlink(socket_path.c_str())) << strerror(errno);
|
||||
|
||||
// Ensure that write() from the client returns an error since the server is closed.
|
||||
ASSERT_EQ(-1, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
|
||||
ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno);
|
||||
|
||||
// Open the server socket again.
|
||||
open_server_socket();
|
||||
|
||||
// Reconnect the same client socket.
|
||||
ASSERT_EQ(0,
|
||||
TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
|
||||
sizeof(client_sockaddr))))
|
||||
<< strerror(errno);
|
||||
|
||||
// Ensure that communication works.
|
||||
ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
|
||||
ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
|
||||
ASSERT_STREQ(kSmoke, read_buf);
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <log/logprint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <log/log_read.h>
|
||||
|
||||
size_t convertPrintable(char* p, const char* message, size_t messageLen);
|
||||
|
||||
TEST(liblog, convertPrintable_ascii) {
|
||||
auto input = "easy string, output same";
|
||||
auto output_size = convertPrintable(nullptr, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(input));
|
||||
|
||||
char output[output_size];
|
||||
|
||||
output_size = convertPrintable(output, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(input));
|
||||
EXPECT_STREQ(input, output);
|
||||
}
|
||||
|
||||
TEST(liblog, convertPrintable_escapes) {
|
||||
// Note that \t is not escaped.
|
||||
auto input = "escape\a\b\t\v\f\r\\";
|
||||
auto expected_output = "escape\\a\\b\t\\v\\f\\r\\\\";
|
||||
auto output_size = convertPrintable(nullptr, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(expected_output));
|
||||
|
||||
char output[output_size];
|
||||
|
||||
output_size = convertPrintable(output, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(expected_output));
|
||||
EXPECT_STREQ(expected_output, output);
|
||||
}
|
||||
|
||||
TEST(liblog, convertPrintable_validutf8) {
|
||||
auto input = u8"¢ह€𐍈";
|
||||
auto output_size = convertPrintable(nullptr, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(input));
|
||||
|
||||
char output[output_size];
|
||||
|
||||
output_size = convertPrintable(output, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(input));
|
||||
EXPECT_STREQ(input, output);
|
||||
}
|
||||
|
||||
TEST(liblog, convertPrintable_invalidutf8) {
|
||||
auto input = "\x80\xC2\x01\xE0\xA4\x06\xE0\x06\xF0\x90\x8D\x06\xF0\x90\x06\xF0\x0E";
|
||||
auto expected_output =
|
||||
"\\x80\\xC2\\x01\\xE0\\xA4\\x06\\xE0\\x06\\xF0\\x90\\x8D\\x06\\xF0\\x90\\x06\\xF0\\x0E";
|
||||
auto output_size = convertPrintable(nullptr, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(expected_output));
|
||||
|
||||
char output[output_size];
|
||||
|
||||
output_size = convertPrintable(output, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(expected_output));
|
||||
EXPECT_STREQ(expected_output, output);
|
||||
}
|
||||
|
||||
TEST(liblog, convertPrintable_mixed) {
|
||||
auto input =
|
||||
u8"\x80\xC2¢ह€𐍈\x01\xE0\xA4\x06¢ह€𐍈\xE0\x06\a\b\xF0\x90¢ह€𐍈\x8D\x06\xF0\t\t\x90\x06\xF0\x0E";
|
||||
auto expected_output =
|
||||
u8"\\x80\\xC2¢ह€𐍈\\x01\\xE0\\xA4\\x06¢ह€𐍈\\xE0\\x06\\a\\b\\xF0\\x90¢ह€𐍈\\x8D\\x06\\xF0\t\t"
|
||||
u8"\\x90\\x06\\xF0\\x0E";
|
||||
auto output_size = convertPrintable(nullptr, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(expected_output));
|
||||
|
||||
char output[output_size];
|
||||
|
||||
output_size = convertPrintable(output, input, strlen(input));
|
||||
EXPECT_EQ(output_size, strlen(expected_output));
|
||||
EXPECT_STREQ(expected_output, output);
|
||||
}
|
||||
|
||||
TEST(liblog, log_print_different_header_size) {
|
||||
constexpr int32_t kPid = 123;
|
||||
constexpr uint32_t kTid = 456;
|
||||
constexpr uint32_t kSec = 1000;
|
||||
constexpr uint32_t kNsec = 999;
|
||||
constexpr uint32_t kLid = LOG_ID_MAIN;
|
||||
constexpr uint32_t kUid = 987;
|
||||
constexpr char kPriority = ANDROID_LOG_ERROR;
|
||||
|
||||
auto create_buf = [](char* buf, size_t len, uint16_t hdr_size) {
|
||||
memset(buf, 0, len);
|
||||
logger_entry* header = reinterpret_cast<logger_entry*>(buf);
|
||||
header->hdr_size = hdr_size;
|
||||
header->pid = kPid;
|
||||
header->tid = kTid;
|
||||
header->sec = kSec;
|
||||
header->nsec = kNsec;
|
||||
header->lid = kLid;
|
||||
header->uid = kUid;
|
||||
char* message = buf + header->hdr_size;
|
||||
uint16_t message_len = 0;
|
||||
message[message_len++] = kPriority;
|
||||
message[message_len++] = 'T';
|
||||
message[message_len++] = 'a';
|
||||
message[message_len++] = 'g';
|
||||
message[message_len++] = '\0';
|
||||
message[message_len++] = 'm';
|
||||
message[message_len++] = 's';
|
||||
message[message_len++] = 'g';
|
||||
message[message_len++] = '!';
|
||||
message[message_len++] = '\0';
|
||||
header->len = message_len;
|
||||
};
|
||||
|
||||
auto check_entry = [&](const AndroidLogEntry& entry) {
|
||||
EXPECT_EQ(kSec, static_cast<uint32_t>(entry.tv_sec));
|
||||
EXPECT_EQ(kNsec, static_cast<uint32_t>(entry.tv_nsec));
|
||||
EXPECT_EQ(kPriority, entry.priority);
|
||||
EXPECT_EQ(kUid, static_cast<uint32_t>(entry.uid));
|
||||
EXPECT_EQ(kPid, entry.pid);
|
||||
EXPECT_EQ(kTid, static_cast<uint32_t>(entry.tid));
|
||||
EXPECT_STREQ("Tag", entry.tag);
|
||||
EXPECT_EQ(4U, entry.tagLen); // Apparently taglen includes the nullptr?
|
||||
EXPECT_EQ(4U, entry.messageLen);
|
||||
EXPECT_STREQ("msg!", entry.message);
|
||||
};
|
||||
alignas(logger_entry) char buf[LOGGER_ENTRY_MAX_LEN];
|
||||
create_buf(buf, sizeof(buf), sizeof(logger_entry));
|
||||
|
||||
AndroidLogEntry entry_normal_size;
|
||||
ASSERT_EQ(0,
|
||||
android_log_processLogBuffer(reinterpret_cast<logger_entry*>(buf), &entry_normal_size));
|
||||
check_entry(entry_normal_size);
|
||||
|
||||
create_buf(buf, sizeof(buf), sizeof(logger_entry) + 3);
|
||||
AndroidLogEntry entry_odd_size;
|
||||
ASSERT_EQ(0, android_log_processLogBuffer(reinterpret_cast<logger_entry*>(buf), &entry_odd_size));
|
||||
check_entry(entry_odd_size);
|
||||
}
|
27
liblog/uio.h
27
liblog/uio.h
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <stddef.h>
|
||||
struct iovec {
|
||||
void* iov_base;
|
||||
size_t iov_len;
|
||||
};
|
||||
#else
|
||||
#include <sys/uio.h>
|
||||
#endif
|
|
@ -1,57 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2006 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_defaults {
|
||||
name: "logcat_defaults",
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libprocessgroup",
|
||||
],
|
||||
static_libs: ["liblog"],
|
||||
logtags: ["event.logtags"],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "logcat",
|
||||
|
||||
defaults: ["logcat_defaults"],
|
||||
srcs: [
|
||||
"logcat.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
sh_binary {
|
||||
name: "logcatd",
|
||||
src: "logcatd",
|
||||
}
|
||||
|
||||
sh_binary {
|
||||
name: "logpersist.start",
|
||||
src: "logpersist",
|
||||
init_rc: ["logcatd.rc"],
|
||||
required: ["logcatd"],
|
||||
symlinks: [
|
||||
"logpersist.stop",
|
||||
"logpersist.cat",
|
||||
],
|
||||
}
|
190
logcat/NOTICE
190
logcat/NOTICE
|
@ -1,190 +0,0 @@
|
|||
|
||||
Copyright (c) 2005-2008, The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
|
@ -1 +0,0 @@
|
|||
tomcherry@google.com
|
|
@ -1,159 +0,0 @@
|
|||
# The entries in this file map a sparse set of log tag numbers to tag names.
|
||||
# This is installed on the device, in /system/etc, and parsed by logcat.
|
||||
#
|
||||
# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
|
||||
# negative values alone for now.)
|
||||
#
|
||||
# Tag names are one or more ASCII letters and numbers or underscores, i.e.
|
||||
# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
|
||||
# impacts log readability, the latter makes regex searches more annoying).
|
||||
#
|
||||
# Tag numbers and names are separated by whitespace. Blank lines and lines
|
||||
# starting with '#' are ignored.
|
||||
#
|
||||
# Optionally, after the tag names can be put a description for the value(s)
|
||||
# of the tag. Description are in the format
|
||||
# (<name>|data type[|data unit])
|
||||
# Multiple values are separated by commas.
|
||||
#
|
||||
# The data type is a number from the following values:
|
||||
# 1: int
|
||||
# 2: long
|
||||
# 3: string
|
||||
# 4: list
|
||||
# 5: float
|
||||
#
|
||||
# The data unit is a number taken from the following list:
|
||||
# 1: Number of objects
|
||||
# 2: Number of bytes
|
||||
# 3: Number of milliseconds
|
||||
# 4: Number of allocations
|
||||
# 5: Id
|
||||
# 6: Percent
|
||||
# s: Number of seconds (monotonic time)
|
||||
# Default value for data of type int/long is 2 (bytes).
|
||||
#
|
||||
# TODO: generate ".java" and ".h" files with integer constants from this file.
|
||||
|
||||
# These are used for testing, do not modify without updating
|
||||
# tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
|
||||
# system/core/liblog/tests/liblog_benchmark.cpp
|
||||
# system/core/liblog/tests/liblog_test.cpp
|
||||
42 answer (to life the universe etc|3)
|
||||
314 pi
|
||||
2718 e
|
||||
|
||||
# "account" is the java hash of the account name
|
||||
2720 sync (id|3),(event|1|5),(source|1|5),(account|1|5)
|
||||
|
||||
# This event is logged when the location service uploads location data.
|
||||
2740 location_controller
|
||||
# This event is logged when someone is deciding to force a garbage collection
|
||||
2741 force_gc (reason|3)
|
||||
# This event is logged on each tickle
|
||||
2742 tickle (authority|3)
|
||||
|
||||
# contacts aggregation: time and number of contacts.
|
||||
# count is negative for query phase, positive for merge phase
|
||||
2747 contacts_aggregation (aggregation time|2|3), (count|1|1)
|
||||
|
||||
# Device boot timings. We include monotonic clock values because the
|
||||
# intrinsic event log times are wall-clock.
|
||||
#
|
||||
# Runtime starts:
|
||||
3000 boot_progress_start (time|2|3)
|
||||
# ZygoteInit class preloading starts:
|
||||
3020 boot_progress_preload_start (time|2|3)
|
||||
# ZygoteInit class preloading ends:
|
||||
3030 boot_progress_preload_end (time|2|3)
|
||||
|
||||
# Dalvik VM / ART
|
||||
20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)
|
||||
20004 art_hidden_api_access (access_method|1),(flags|1),(class|3),(member|3),(type_signature|3)
|
||||
|
||||
75000 sqlite_mem_alarm_current (current|1|2)
|
||||
75001 sqlite_mem_alarm_max (max|1|2)
|
||||
75002 sqlite_mem_alarm_alloc_attempt (attempts|1|4)
|
||||
75003 sqlite_mem_released (Memory released|1|2)
|
||||
75004 sqlite_db_corrupt (Database file corrupt|3)
|
||||
|
||||
50000 menu_item_selected (Menu type where 0 is options and 1 is context|1|5),(Menu item title|3)
|
||||
50001 menu_opened (Menu type where 0 is options and 1 is context|1|5)
|
||||
|
||||
# HSM wifi state change
|
||||
# Hierarchical state class name (as defined in WifiStateTracker.java)
|
||||
# Logged on every state change in the hierarchical state machine
|
||||
50021 wifi_state_changed (wifi_state|3)
|
||||
# HSM wifi event
|
||||
# [31-16] Reserved for future use
|
||||
# [15 - 0] HSM event (as defined in WifiStateTracker.java)
|
||||
# Logged when an event is handled in a hierarchical state
|
||||
50022 wifi_event_handled (wifi_event|1|5)
|
||||
# Supplicant state change
|
||||
# [31-13] Reserved for future use
|
||||
# [8 - 0] Supplicant state (as defined in SupplicantState.java)
|
||||
# Logged when the supplicant switches to a new state
|
||||
50023 wifi_supplicant_state_changed (supplicant_state|1|5)
|
||||
|
||||
# Database operation samples.
|
||||
# db: the filename of the database
|
||||
# sql: the executed query (without query args)
|
||||
# time: cpu time millis (not wall time), including lock acquisition
|
||||
# blocking_package: if this is on a main thread, the package name, otherwise ""
|
||||
# sample_percent: the percent likelihood this query was logged
|
||||
52000 db_sample (db|3),(sql|3),(time|1|3),(blocking_package|3),(sample_percent|1|6)
|
||||
|
||||
# http request/response stats
|
||||
52001 http_stats (useragent|3),(response|2|3),(processing|2|3),(tx|1|2),(rx|1|2)
|
||||
60000 viewroot_draw (Draw time|1|3)
|
||||
60001 viewroot_layout (Layout time|1|3)
|
||||
60002 view_build_drawing_cache (View created drawing cache|1|5)
|
||||
60003 view_use_drawing_cache (View drawn using bitmap cache|1|5)
|
||||
|
||||
# graphics timestamp
|
||||
# 60100 - 60199 reserved for surfaceflinger
|
||||
|
||||
# audio
|
||||
# 61000 - 61199 reserved for audioserver
|
||||
|
||||
# input
|
||||
# 62000 - 62199 reserved for inputflinger
|
||||
|
||||
# com.android.server.policy
|
||||
# 70000 - 70199 reserved for PhoneWindowManager and other policies
|
||||
|
||||
# aggregation service
|
||||
70200 aggregation (aggregation time|2|3)
|
||||
70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2)
|
||||
|
||||
# gms refuses to register this log tag, b/30156345
|
||||
70220 gms_unknown
|
||||
|
||||
# libc failure logging
|
||||
80100 bionic_event_memcpy_buffer_overflow (uid|1)
|
||||
80105 bionic_event_strcat_buffer_overflow (uid|1)
|
||||
80110 bionic_event_memmov_buffer_overflow (uid|1)
|
||||
80115 bionic_event_strncat_buffer_overflow (uid|1)
|
||||
80120 bionic_event_strncpy_buffer_overflow (uid|1)
|
||||
80125 bionic_event_memset_buffer_overflow (uid|1)
|
||||
80130 bionic_event_strcpy_buffer_overflow (uid|1)
|
||||
|
||||
80200 bionic_event_strcat_integer_overflow (uid|1)
|
||||
80205 bionic_event_strncat_integer_overflow (uid|1)
|
||||
|
||||
80300 bionic_event_resolver_old_response (uid|1)
|
||||
80305 bionic_event_resolver_wrong_server (uid|1)
|
||||
80310 bionic_event_resolver_wrong_query (uid|1)
|
||||
|
||||
# libcore failure logging
|
||||
90100 exp_det_cert_pin_failure (certs|4)
|
||||
|
||||
# 150000 - 160000 reserved for Android Automotive builds
|
||||
|
||||
1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
|
||||
|
||||
# for events that go to stats log buffer
|
||||
1937006964 stats_log (atom_id|1|5),(data|4)
|
||||
|
||||
# NOTE - the range 1000000-2000000 is reserved for partners and others who
|
||||
# want to define their own log tags without conflicting with the core platform.
|
1212
logcat/logcat.cpp
1212
logcat/logcat.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,29 +0,0 @@
|
|||
#! /system/bin/sh
|
||||
|
||||
# This is primarily meant to be used by logpersist. This script is run as an init service, which
|
||||
# first reads the 'last' logcat to persistent storage with `-L` then run logcat again without
|
||||
# `-L` to read the current logcat buffers to persistent storage.
|
||||
|
||||
# init sets the umask to 077 for forked processes. logpersist needs to create files that are group
|
||||
# readable. So relax the umask to only disallow group wx and world rwx.
|
||||
umask 037
|
||||
|
||||
has_last="false"
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" == "-L" -o "$arg" == "--last" ]; then
|
||||
has_last="true"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$has_last" == "true" ]; then
|
||||
logcat "$@"
|
||||
fi
|
||||
|
||||
args_without_last=()
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" != "-L" -a "$arg" != "--last" ]; then
|
||||
ARGS+=("$arg")
|
||||
fi
|
||||
done
|
||||
|
||||
exec logcat "${ARGS[@]}"
|
|
@ -1,62 +0,0 @@
|
|||
#
|
||||
# init scriptures for logcatd persistent logging.
|
||||
#
|
||||
# Make sure any property changes are only performed with /data mounted, after
|
||||
# post-fs-data state because otherwise behavior is undefined. The exceptions
|
||||
# are device adjustments for logcatd service properties (persist.* overrides
|
||||
# notwithstanding) for logd.logpersistd.size logd.logpersistd.rotate_kbytes and
|
||||
# logd.logpersistd.buffer.
|
||||
|
||||
# persist to non-persistent trampolines to permit device properties can be
|
||||
# overridden when /data mounts, or during runtime.
|
||||
on property:persist.logd.logpersistd.count=*
|
||||
# expect /init to report failure if property empty (default)
|
||||
setprop persist.logd.logpersistd.size ${persist.logd.logpersistd.count}
|
||||
|
||||
on property:persist.logd.logpersistd.size=*
|
||||
setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
|
||||
|
||||
on property:persist.logd.logpersistd.rotate_kbytes=*
|
||||
setprop logd.logpersistd.rotate_kbytes ${persist.logd.logpersistd.rotate_kbytes}
|
||||
|
||||
on property:persist.logd.logpersistd.buffer=*
|
||||
setprop logd.logpersistd.buffer ${persist.logd.logpersistd.buffer}
|
||||
|
||||
on property:persist.logd.logpersistd=logcatd
|
||||
setprop logd.logpersistd logcatd
|
||||
|
||||
# enable, prep and start logcatd service
|
||||
on load_persist_props_action
|
||||
setprop logd.logpersistd.enable true
|
||||
|
||||
on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
|
||||
# log group should be able to read persisted logs
|
||||
mkdir /data/misc/logd 0750 logd log
|
||||
start logcatd
|
||||
|
||||
# stop logcatd service and clear data
|
||||
on property:logd.logpersistd.enable=true && property:logd.logpersistd=clear
|
||||
setprop persist.logd.logpersistd ""
|
||||
stop logcatd
|
||||
# logd for clear of only our files in /data/misc/logd
|
||||
exec - logd log -- /system/bin/logcat -c -f /data/misc/logd/logcat -n ${logd.logpersistd.size:-256}
|
||||
setprop logd.logpersistd ""
|
||||
|
||||
# stop logcatd service
|
||||
on property:logd.logpersistd=stop
|
||||
setprop persist.logd.logpersistd ""
|
||||
stop logcatd
|
||||
setprop logd.logpersistd ""
|
||||
|
||||
on property:logd.logpersistd.enable=false
|
||||
stop logcatd
|
||||
|
||||
# logcatd service
|
||||
service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r ${logd.logpersistd.rotate_kbytes:-2048} -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
|
||||
class late_start
|
||||
disabled
|
||||
# logd for write to /data/misc/logd, log group for read from log daemon
|
||||
user logd
|
||||
group log
|
||||
writepid /dev/cpuset/system-background/tasks
|
||||
oom_score_adjust -600
|
|
@ -1,178 +0,0 @@
|
|||
#! /system/bin/sh
|
||||
# logpersist cat, start and stop handlers
|
||||
progname="${0##*/}"
|
||||
case `getprop ro.debuggable` in
|
||||
1) ;;
|
||||
*) echo "${progname} - Permission denied"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
property=persist.logd.logpersistd
|
||||
|
||||
case `getprop ${property#persist.}.enable` in
|
||||
true) ;;
|
||||
*) echo "${progname} - Disabled"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log_uid=logd
|
||||
log_tag_property=persist.log.tag
|
||||
data=/data/misc/logd/logcat
|
||||
service=logcatd
|
||||
size_default=256
|
||||
buffer_default=all
|
||||
args="${@}"
|
||||
|
||||
size=${size_default}
|
||||
buffer=${buffer_default}
|
||||
clear=false
|
||||
while [ ${#} -gt 0 ]; do
|
||||
case ${1} in
|
||||
-c|--clear) clear=true ;;
|
||||
--size=*) size="${1#--size=}" ;;
|
||||
--rotate-count=*) size="${1#--rotate-count=}" ;;
|
||||
-n|--size|--rotate-count) size="${2}" ; shift ;;
|
||||
--buffer=*) buffer="${1#--buffer=}" ;;
|
||||
-b|--buffer) buffer="${2}" ; shift ;;
|
||||
-h|--help|*)
|
||||
LEAD_SPACE_="`echo ${progname%.*} | tr '[ -~]' ' '`"
|
||||
echo "${progname%.*}.cat - dump current ${service} logs"
|
||||
echo "${progname%.*}.start [--size=<size_in_kb>] [--buffer=<buffers>] [--clear]"
|
||||
echo "${LEAD_SPACE_} - start ${service} service"
|
||||
echo "${progname%.*}.stop [--clear] - stop ${service} service"
|
||||
case ${1} in
|
||||
-h|--help) exit 0 ;;
|
||||
*) echo ERROR: bad argument ${@} >&2 ; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "${size}" -o "${size_default}" = "${size}" ]; then
|
||||
unset size
|
||||
fi
|
||||
if [ -n "${size}" ] &&
|
||||
! ( [ 0 -lt "${size}" ] && [ 2048 -ge "${size}" ] ) >/dev/null 2>&1; then
|
||||
echo ERROR: Invalid --size ${size} >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${buffer}" -o "${buffer_default}" = "${buffer}" ]; then
|
||||
unset buffer
|
||||
fi
|
||||
if [ -n "${buffer}" ] && ! logcat -b ${buffer} -g >/dev/null 2>&1; then
|
||||
echo ERROR: Invalid --buffer ${buffer} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_tag="`getprop ${log_tag_property}`"
|
||||
logd_logpersistd="`getprop ${property}`"
|
||||
|
||||
case ${progname} in
|
||||
*.cat)
|
||||
if [ -n "${size}${buffer}" -o "true" = "${clear}" ]; then
|
||||
echo WARNING: Can not use --clear, --size or --buffer with ${progname%.*}.cat >&2
|
||||
fi
|
||||
su ${log_uid} ls "${data%/*}" |
|
||||
tr -d '\r' |
|
||||
sort -ru |
|
||||
sed "s#^#${data%/*}/#" |
|
||||
grep "${data}[.]*[0-9]*\$" |
|
||||
su ${log_uid} xargs cat
|
||||
;;
|
||||
*.start)
|
||||
current_buffer="`getprop ${property#persist.}.buffer`"
|
||||
current_size="`getprop ${property#persist.}.size`"
|
||||
if [ "${service}" = "`getprop ${property#persist.}`" ]; then
|
||||
if [ "true" = "${clear}" ]; then
|
||||
setprop ${property#persist.} "clear"
|
||||
elif [ "${buffer}|${size}" != "${current_buffer}|${current_size}" ]; then
|
||||
echo "ERROR: Changing existing collection parameters from" >&2
|
||||
if [ "${buffer}" != "${current_buffer}" ]; then
|
||||
a=${current_buffer}
|
||||
b=${buffer}
|
||||
if [ -z "${a}" ]; then a="${default_buffer}"; fi
|
||||
if [ -z "${b}" ]; then b="${default_buffer}"; fi
|
||||
echo " --buffer ${a} to ${b}" >&2
|
||||
fi
|
||||
if [ "${size}" != "${current_size}" ]; then
|
||||
a=${current_size}
|
||||
b=${size}
|
||||
if [ -z "${a}" ]; then a="${default_size}"; fi
|
||||
if [ -z "${b}" ]; then b="${default_size}"; fi
|
||||
echo " --size ${a} to ${b}" >&2
|
||||
fi
|
||||
echo " Are you sure you want to do this?" >&2
|
||||
echo " Suggest add --clear to erase data and restart with new settings." >&2
|
||||
echo " To blindly override and retain data, ${progname%.*}.stop first." >&2
|
||||
exit 1
|
||||
fi
|
||||
elif [ "true" = "${clear}" ]; then
|
||||
setprop ${property#persist.} "clear"
|
||||
fi
|
||||
if [ -n "${buffer}${current_buffer}" ]; then
|
||||
setprop ${property}.buffer "${buffer}"
|
||||
if [ -z "${buffer}" ]; then
|
||||
# deal with trampoline for empty properties
|
||||
setprop ${property#persist.}.buffer ""
|
||||
fi
|
||||
fi
|
||||
if [ -n "${size}${current_size}" ]; then
|
||||
setprop ${property}.size "${size}"
|
||||
if [ -z "${size}" ]; then
|
||||
# deal with trampoline for empty properties
|
||||
setprop ${property#persist.}.size ""
|
||||
fi
|
||||
fi
|
||||
while [ "clear" = "`getprop ${property#persist.}`" ]; do
|
||||
continue
|
||||
done
|
||||
# Tell Settings that we are back on again if we turned logging off
|
||||
tag="${log_tag#Settings}"
|
||||
if [ X"${log_tag}" != X"${tag}" ]; then
|
||||
echo "WARNING: enabling logd service" >&2
|
||||
setprop ${log_tag_property} "${tag#,}"
|
||||
fi
|
||||
# ${service}.rc does the heavy lifting with the following trigger
|
||||
setprop ${property} ${service}
|
||||
# 20ms done, to permit process feedback check
|
||||
sleep 1
|
||||
getprop ${property#persist.}
|
||||
# also generate an error return code if not found running
|
||||
pgrep -u ${log_uid} ${service%d}
|
||||
;;
|
||||
*.stop)
|
||||
if [ -n "${size}${buffer}" ]; then
|
||||
echo "WARNING: Can not use --size or --buffer with ${progname%.*}.stop" >&2
|
||||
fi
|
||||
if [ "true" = "${clear}" ]; then
|
||||
setprop ${property#persist.} "clear"
|
||||
else
|
||||
setprop ${property#persist.} "stop"
|
||||
fi
|
||||
if [ -n "`getprop ${property#persist.}.buffer`" ]; then
|
||||
setprop ${property}.buffer ""
|
||||
# deal with trampoline for empty properties
|
||||
setprop ${property#persist.}.buffer ""
|
||||
fi
|
||||
if [ -n "`getprop ${property#persist.}.size`" ]; then
|
||||
setprop ${property}.size ""
|
||||
# deal with trampoline for empty properties
|
||||
setprop ${property#persist.}.size ""
|
||||
fi
|
||||
while [ "clear" = "`getprop ${property#persist.}`" ]; do
|
||||
continue
|
||||
done
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unexpected command ${0##*/} ${args}" >&2
|
||||
exit 1
|
||||
esac
|
||||
|
||||
if [ X"${log_tag}" != X"`getprop ${log_tag_property}`" ] ||
|
||||
[ X"${logd_logpersistd}" != X"`getprop ${property}`" ]; then
|
||||
echo "WARNING: killing Settings application to pull in new values" >&2
|
||||
am force-stop com.android.settings
|
||||
fi
|
|
@ -1,57 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2013-2014 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_defaults {
|
||||
name: "logcat-tests-defaults",
|
||||
cflags: [
|
||||
"-fstack-protector-all",
|
||||
"-g",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fno-builtin",
|
||||
],
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Benchmarks
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Build benchmarks for the device. Run with:
|
||||
// adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
|
||||
cc_benchmark {
|
||||
name: "logcat-benchmarks",
|
||||
defaults: ["logcat-tests-defaults"],
|
||||
srcs: ["logcat_benchmark.cpp"],
|
||||
shared_libs: ["libbase"],
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Unit tests.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Build tests for the device (with .so). Run with:
|
||||
// adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
|
||||
cc_test {
|
||||
name: "logcat-unit-tests",
|
||||
defaults: ["logcat-tests-defaults"],
|
||||
shared_libs: ["libbase"],
|
||||
static_libs: ["liblog"],
|
||||
srcs: [
|
||||
"logcat_test.cpp",
|
||||
"logcatd_test.cpp",
|
||||
],
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
static const char begin[] = "--------- beginning of ";
|
||||
|
||||
static void BM_logcat_sorted_order(benchmark::State& state) {
|
||||
FILE* fp;
|
||||
|
||||
if (!state.KeepRunning()) return;
|
||||
|
||||
fp = popen(
|
||||
"logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
|
||||
"r");
|
||||
if (!fp) return;
|
||||
|
||||
class timestamp {
|
||||
private:
|
||||
int month;
|
||||
int day;
|
||||
int hour;
|
||||
int minute;
|
||||
int second;
|
||||
int millisecond;
|
||||
bool ok;
|
||||
|
||||
public:
|
||||
void init(const char* buffer) {
|
||||
ok = false;
|
||||
if (buffer != NULL) {
|
||||
ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", &month, &day, &hour,
|
||||
&minute, &second, &millisecond) == 6;
|
||||
}
|
||||
}
|
||||
|
||||
explicit timestamp(const char* buffer) {
|
||||
init(buffer);
|
||||
}
|
||||
|
||||
bool operator<(timestamp& T) {
|
||||
return !ok || !T.ok || (month < T.month) ||
|
||||
((month == T.month) &&
|
||||
((day < T.day) ||
|
||||
((day == T.day) &&
|
||||
((hour < T.hour) ||
|
||||
((hour == T.hour) &&
|
||||
((minute < T.minute) ||
|
||||
((minute == T.minute) &&
|
||||
((second < T.second) ||
|
||||
((second == T.second) &&
|
||||
(millisecond < T.millisecond))))))))));
|
||||
}
|
||||
|
||||
bool valid(void) {
|
||||
return ok;
|
||||
}
|
||||
} last(NULL);
|
||||
|
||||
char* last_buffer = NULL;
|
||||
char buffer[5120];
|
||||
|
||||
int count = 0;
|
||||
int next_lt_last = 0;
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), fp)) {
|
||||
if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
|
||||
continue;
|
||||
}
|
||||
if (!last.valid()) {
|
||||
free(last_buffer);
|
||||
last_buffer = strdup(buffer);
|
||||
last.init(buffer);
|
||||
}
|
||||
timestamp next(buffer);
|
||||
if (next < last) {
|
||||
if (last_buffer) {
|
||||
fprintf(stderr, "<%s", last_buffer);
|
||||
}
|
||||
fprintf(stderr, ">%s", buffer);
|
||||
++next_lt_last;
|
||||
}
|
||||
if (next.valid()) {
|
||||
free(last_buffer);
|
||||
last_buffer = strdup(buffer);
|
||||
last.init(buffer);
|
||||
}
|
||||
++count;
|
||||
}
|
||||
free(last_buffer);
|
||||
|
||||
pclose(fp);
|
||||
|
||||
static const int max_ok = 2;
|
||||
|
||||
// Allow few fails, happens with readers active
|
||||
fprintf(stderr, "%s: %d/%d out of order entries\n",
|
||||
(next_lt_last) ? ((next_lt_last <= max_ok) ? "WARNING" : "ERROR")
|
||||
: "INFO",
|
||||
next_lt_last, count);
|
||||
|
||||
if (next_lt_last > max_ok) {
|
||||
fprintf(stderr, "EXPECT_GE(max_ok=%d, next_lt_last=%d)\n", max_ok,
|
||||
next_lt_last);
|
||||
}
|
||||
|
||||
// sample statistically too small
|
||||
if (count < 100) {
|
||||
fprintf(stderr, "EXPECT_LT(100, count=%d)\n", count);
|
||||
}
|
||||
|
||||
state.KeepRunning();
|
||||
}
|
||||
BENCHMARK(BM_logcat_sorted_order);
|
||||
|
||||
BENCHMARK_MAIN();
|
File diff suppressed because it is too large
Load Diff
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define logcat logcatd
|
||||
#define logcat_executable "logcatd"
|
||||
|
||||
#include "logcat_test.cpp"
|
|
@ -1 +0,0 @@
|
|||
../.clang-format-4
|
212
logd/Android.bp
212
logd/Android.bp
|
@ -1,212 +0,0 @@
|
|||
// Copyright (C) 2017 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is what we want to do:
|
||||
// event_logtags = $(shell
|
||||
// sed -n
|
||||
// "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p"
|
||||
// $(LOCAL_PATH)/$2/event.logtags)
|
||||
// event_flag := $(call event_logtags,auditd)
|
||||
// event_flag += $(call event_logtags,logd)
|
||||
// event_flag += $(call event_logtags,tag_def)
|
||||
// so make sure we do not regret hard-coding it as follows:
|
||||
event_flag = [
|
||||
"-DAUDITD_LOG_TAG=1003",
|
||||
"-DCHATTY_LOG_TAG=1004",
|
||||
"-DTAG_DEF_LOG_TAG=1005",
|
||||
"-DLIBLOG_LOG_TAG=1006",
|
||||
]
|
||||
|
||||
cc_defaults {
|
||||
name: "logd_defaults",
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libz",
|
||||
],
|
||||
static_libs: ["libzstd"],
|
||||
header_libs: ["libcutils_headers"],
|
||||
cflags: [
|
||||
"-Wextra",
|
||||
"-Wthread-safety",
|
||||
] + event_flag,
|
||||
|
||||
lto: {
|
||||
thin: true,
|
||||
},
|
||||
sanitize: {
|
||||
cfi: true,
|
||||
},
|
||||
cpp_std: "experimental",
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "liblogd",
|
||||
defaults: ["logd_defaults"],
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"ChattyLogBuffer.cpp",
|
||||
"CompressionEngine.cpp",
|
||||
"LogBufferElement.cpp",
|
||||
"LogReaderList.cpp",
|
||||
"LogReaderThread.cpp",
|
||||
"LogSize.cpp",
|
||||
"LogStatistics.cpp",
|
||||
"LogTags.cpp",
|
||||
"LogdLock.cpp",
|
||||
"PruneList.cpp",
|
||||
"SerializedFlushToState.cpp",
|
||||
"SerializedLogBuffer.cpp",
|
||||
"SerializedLogChunk.cpp",
|
||||
"SimpleLogBuffer.cpp",
|
||||
],
|
||||
static_libs: ["liblog"],
|
||||
logtags: ["event.logtags"],
|
||||
|
||||
export_include_dirs: ["."],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "logd",
|
||||
defaults: ["logd_defaults"],
|
||||
init_rc: ["logd.rc"],
|
||||
|
||||
srcs: [
|
||||
"main.cpp",
|
||||
"LogPermissions.cpp",
|
||||
"CommandListener.cpp",
|
||||
"LogListener.cpp",
|
||||
"LogReader.cpp",
|
||||
"LogAudit.cpp",
|
||||
"LogKlog.cpp",
|
||||
"libaudit.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"liblog",
|
||||
"liblogd",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libsysutils",
|
||||
"libcutils",
|
||||
"libpackagelistparser",
|
||||
"libprocessgroup",
|
||||
"libcap",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "auditctl",
|
||||
|
||||
srcs: [
|
||||
"auditctl.cpp",
|
||||
"libaudit.cpp",
|
||||
],
|
||||
|
||||
shared_libs: ["libbase"],
|
||||
|
||||
cflags: [
|
||||
"-Wextra",
|
||||
],
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "logtagd.rc",
|
||||
src: "logtagd.rc",
|
||||
sub_dir: "init",
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Unit tests.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
cc_defaults {
|
||||
name: "logd-unit-test-defaults",
|
||||
|
||||
cflags: [
|
||||
"-fstack-protector-all",
|
||||
"-g",
|
||||
"-Wall",
|
||||
"-Wthread-safety",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fno-builtin",
|
||||
] + event_flag,
|
||||
|
||||
srcs: [
|
||||
"ChattyLogBufferTest.cpp",
|
||||
"logd_test.cpp",
|
||||
"LogBufferTest.cpp",
|
||||
"SerializedLogChunkTest.cpp",
|
||||
"SerializedFlushToStateTest.cpp",
|
||||
],
|
||||
sanitize: {
|
||||
cfi: true,
|
||||
},
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
"liblogd",
|
||||
"libselinux",
|
||||
"libz",
|
||||
"libzstd",
|
||||
],
|
||||
}
|
||||
|
||||
// Build tests for the logger. Run with:
|
||||
// adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
|
||||
cc_test {
|
||||
name: "logd-unit-tests",
|
||||
host_supported: true,
|
||||
defaults: ["logd-unit-test-defaults"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "CtsLogdTestCases",
|
||||
defaults: ["logd-unit-test-defaults"],
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
test_suites: [
|
||||
"cts",
|
||||
"device-tests",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "replay_messages",
|
||||
defaults: ["logd_defaults"],
|
||||
host_supported: true,
|
||||
|
||||
srcs: [
|
||||
"ReplayMessages.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
"liblogd",
|
||||
"libselinux",
|
||||
"libz",
|
||||
"libzstd",
|
||||
],
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<configuration description="Config for CTS Logging Daemon test cases">
|
||||
<option name="test-suite-tag" value="cts" />
|
||||
<option name="config-descriptor:metadata" key="component" value="systems" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
|
||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
|
||||
<option name="append-bitness" value="true" />
|
||||
</target_preparer>
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="native-test-device-path" value="/data/local/tmp" />
|
||||
<option name="module-name" value="CtsLogdTestCases" />
|
||||
<option name="runtime-hint" value="65s" />
|
||||
</test>
|
||||
</configuration>
|
|
@ -1,617 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// for manual checking of stale entries during ChattyLogBuffer::erase()
|
||||
//#define DEBUG_CHECK_FOR_STALE_ENTRIES
|
||||
|
||||
#include "ChattyLogBuffer.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/user.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "LogUtils.h"
|
||||
|
||||
#ifndef __predict_false
|
||||
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
|
||||
#endif
|
||||
|
||||
ChattyLogBuffer::ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
|
||||
LogStatistics* stats)
|
||||
: SimpleLogBuffer(reader_list, tags, stats), prune_(prune) {}
|
||||
|
||||
ChattyLogBuffer::~ChattyLogBuffer() {}
|
||||
|
||||
enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
|
||||
|
||||
static enum match_type Identical(const LogBufferElement& elem, const LogBufferElement& last) {
|
||||
ssize_t lenl = elem.msg_len();
|
||||
if (lenl <= 0) return DIFFERENT; // value if this represents a chatty elem
|
||||
ssize_t lenr = last.msg_len();
|
||||
if (lenr <= 0) return DIFFERENT; // value if this represents a chatty elem
|
||||
if (elem.uid() != last.uid()) return DIFFERENT;
|
||||
if (elem.pid() != last.pid()) return DIFFERENT;
|
||||
if (elem.tid() != last.tid()) return DIFFERENT;
|
||||
|
||||
// last is more than a minute old, stop squashing identical messages
|
||||
if (elem.realtime().nsec() > (last.realtime().nsec() + 60 * NS_PER_SEC)) return DIFFERENT;
|
||||
|
||||
// Identical message
|
||||
const char* msgl = elem.msg();
|
||||
const char* msgr = last.msg();
|
||||
if (lenl == lenr) {
|
||||
if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
|
||||
// liblog tagged messages (content gets summed)
|
||||
if (elem.log_id() == LOG_ID_EVENTS && lenl == sizeof(android_log_event_int_t) &&
|
||||
!fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
|
||||
elem.GetTag() == LIBLOG_LOG_TAG) {
|
||||
return SAME_LIBLOG;
|
||||
}
|
||||
}
|
||||
|
||||
// audit message (except sequence number) identical?
|
||||
if (IsBinary(last.log_id()) &&
|
||||
lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t)) &&
|
||||
lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t))) {
|
||||
if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) - sizeof(int32_t))) {
|
||||
return DIFFERENT;
|
||||
}
|
||||
msgl += sizeof(android_log_event_string_t);
|
||||
lenl -= sizeof(android_log_event_string_t);
|
||||
msgr += sizeof(android_log_event_string_t);
|
||||
lenr -= sizeof(android_log_event_string_t);
|
||||
}
|
||||
static const char avc[] = "): avc: ";
|
||||
const char* avcl = android::strnstr(msgl, lenl, avc);
|
||||
if (!avcl) return DIFFERENT;
|
||||
lenl -= avcl - msgl;
|
||||
const char* avcr = android::strnstr(msgr, lenr, avc);
|
||||
if (!avcr) return DIFFERENT;
|
||||
lenr -= avcr - msgr;
|
||||
if (lenl != lenr) return DIFFERENT;
|
||||
if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc), lenl - strlen(avc))) {
|
||||
return DIFFERENT;
|
||||
}
|
||||
return SAME;
|
||||
}
|
||||
|
||||
void ChattyLogBuffer::LogInternal(LogBufferElement&& elem) {
|
||||
// b/137093665: don't coalesce security messages.
|
||||
if (elem.log_id() == LOG_ID_SECURITY) {
|
||||
SimpleLogBuffer::LogInternal(std::move(elem));
|
||||
return;
|
||||
}
|
||||
int log_id = elem.log_id();
|
||||
|
||||
// Initialize last_logged_elements_ to a copy of elem if logging the first element for a log_id.
|
||||
if (!last_logged_elements_[log_id]) {
|
||||
last_logged_elements_[log_id].emplace(elem);
|
||||
SimpleLogBuffer::LogInternal(std::move(elem));
|
||||
return;
|
||||
}
|
||||
|
||||
LogBufferElement& current_last = *last_logged_elements_[log_id];
|
||||
enum match_type match = Identical(elem, current_last);
|
||||
|
||||
if (match == DIFFERENT) {
|
||||
if (duplicate_elements_[log_id]) {
|
||||
// If we previously had 3+ identical messages, log the chatty message.
|
||||
if (duplicate_elements_[log_id]->dropped_count() > 0) {
|
||||
SimpleLogBuffer::LogInternal(std::move(*duplicate_elements_[log_id]));
|
||||
}
|
||||
duplicate_elements_[log_id].reset();
|
||||
// Log the saved copy of the last identical message seen.
|
||||
SimpleLogBuffer::LogInternal(std::move(current_last));
|
||||
}
|
||||
last_logged_elements_[log_id].emplace(elem);
|
||||
SimpleLogBuffer::LogInternal(std::move(elem));
|
||||
return;
|
||||
}
|
||||
|
||||
// 2 identical message: set duplicate_elements_ appropriately.
|
||||
if (!duplicate_elements_[log_id]) {
|
||||
duplicate_elements_[log_id].emplace(std::move(current_last));
|
||||
last_logged_elements_[log_id].emplace(std::move(elem));
|
||||
return;
|
||||
}
|
||||
|
||||
// 3+ identical LIBLOG event messages: coalesce them into last_logged_elements_.
|
||||
if (match == SAME_LIBLOG) {
|
||||
const android_log_event_int_t* current_last_event =
|
||||
reinterpret_cast<const android_log_event_int_t*>(current_last.msg());
|
||||
int64_t current_last_count = current_last_event->payload.data;
|
||||
android_log_event_int_t* elem_event =
|
||||
reinterpret_cast<android_log_event_int_t*>(const_cast<char*>(elem.msg()));
|
||||
int64_t elem_count = elem_event->payload.data;
|
||||
|
||||
int64_t total = current_last_count + elem_count;
|
||||
if (total > std::numeric_limits<int32_t>::max()) {
|
||||
SimpleLogBuffer::LogInternal(std::move(current_last));
|
||||
last_logged_elements_[log_id].emplace(std::move(elem));
|
||||
return;
|
||||
}
|
||||
stats()->AddTotal(current_last.log_id(), current_last.msg_len());
|
||||
elem_event->payload.data = total;
|
||||
last_logged_elements_[log_id].emplace(std::move(elem));
|
||||
return;
|
||||
}
|
||||
|
||||
// 3+ identical messages (not LIBLOG) messages: increase the drop count.
|
||||
uint16_t dropped_count = duplicate_elements_[log_id]->dropped_count();
|
||||
if (dropped_count == std::numeric_limits<uint16_t>::max()) {
|
||||
SimpleLogBuffer::LogInternal(std::move(*duplicate_elements_[log_id]));
|
||||
dropped_count = 0;
|
||||
}
|
||||
// We're dropping the current_last log so add its stats to the total.
|
||||
stats()->AddTotal(current_last.log_id(), current_last.msg_len());
|
||||
// Use current_last for tracking the dropped count to always use the latest timestamp.
|
||||
current_last.SetDropped(dropped_count + 1);
|
||||
duplicate_elements_[log_id].emplace(std::move(current_last));
|
||||
last_logged_elements_[log_id].emplace(std::move(elem));
|
||||
}
|
||||
|
||||
LogBufferElementCollection::iterator ChattyLogBuffer::Erase(LogBufferElementCollection::iterator it,
|
||||
bool coalesce) {
|
||||
LogBufferElement& element = *it;
|
||||
log_id_t id = element.log_id();
|
||||
|
||||
// Remove iterator references in the various lists that will become stale
|
||||
// after the element is erased from the main logging list.
|
||||
|
||||
{ // start of scope for found iterator
|
||||
int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.GetTag() : element.uid();
|
||||
LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
|
||||
if ((found != mLastWorst[id].end()) && (it == found->second)) {
|
||||
mLastWorst[id].erase(found);
|
||||
}
|
||||
}
|
||||
|
||||
{ // start of scope for pid found iterator
|
||||
// element->uid() may not be AID_SYSTEM for next-best-watermark.
|
||||
// will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
|
||||
// long term code stability, find() check should be fast for those ids.
|
||||
LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(element.pid());
|
||||
if (found != mLastWorstPidOfSystem[id].end() && it == found->second) {
|
||||
mLastWorstPidOfSystem[id].erase(found);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
|
||||
LogBufferElementCollection::iterator bad = it;
|
||||
int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->GetTag() : element->uid();
|
||||
#endif
|
||||
|
||||
if (coalesce) {
|
||||
stats()->Erase(element.ToLogStatisticsElement());
|
||||
} else {
|
||||
stats()->Subtract(element.ToLogStatisticsElement());
|
||||
}
|
||||
|
||||
it = SimpleLogBuffer::Erase(it);
|
||||
|
||||
#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
|
||||
log_id_for_each(i) {
|
||||
for (auto b : mLastWorst[i]) {
|
||||
if (bad == b.second) {
|
||||
LOG(ERROR) << StringPrintf("stale mLastWorst[%d] key=%d mykey=%d", i, b.first, key);
|
||||
}
|
||||
}
|
||||
for (auto b : mLastWorstPidOfSystem[i]) {
|
||||
if (bad == b.second) {
|
||||
LOG(ERROR) << StringPrintf("stale mLastWorstPidOfSystem[%d] pid=%d", i, b.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return it;
|
||||
}
|
||||
|
||||
// Define a temporary mechanism to report the last LogBufferElement pointer
|
||||
// for the specified uid, pid and tid. Used below to help merge-sort when
|
||||
// pruning for worst UID.
|
||||
class LogBufferElementLast {
|
||||
typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
|
||||
LogBufferElementMap map;
|
||||
|
||||
public:
|
||||
bool coalesce(LogBufferElement* element, uint16_t dropped) {
|
||||
uint64_t key = LogBufferElementKey(element->uid(), element->pid(), element->tid());
|
||||
LogBufferElementMap::iterator it = map.find(key);
|
||||
if (it != map.end()) {
|
||||
LogBufferElement* found = it->second;
|
||||
uint16_t moreDropped = found->dropped_count();
|
||||
if ((dropped + moreDropped) > USHRT_MAX) {
|
||||
map.erase(it);
|
||||
} else {
|
||||
found->SetDropped(dropped + moreDropped);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void add(LogBufferElement* element) {
|
||||
uint64_t key = LogBufferElementKey(element->uid(), element->pid(), element->tid());
|
||||
map[key] = element;
|
||||
}
|
||||
|
||||
void clear() { map.clear(); }
|
||||
|
||||
void clear(LogBufferElement* element) {
|
||||
uint64_t current = element->realtime().nsec() - (EXPIRE_RATELIMIT * NS_PER_SEC);
|
||||
for (LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
|
||||
LogBufferElement* mapElement = it->second;
|
||||
if (mapElement->dropped_count() >= EXPIRE_THRESHOLD &&
|
||||
current > mapElement->realtime().nsec()) {
|
||||
it = map.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid) {
|
||||
return uint64_t(uid) << 32 | uint64_t(pid) << 16 | uint64_t(tid);
|
||||
}
|
||||
};
|
||||
|
||||
// prune "pruneRows" of type "id" from the buffer.
|
||||
//
|
||||
// This garbage collection task is used to expire log entries. It is called to
|
||||
// remove all logs (clear), all UID logs (unprivileged clear), or every
|
||||
// 256 or 10% of the total logs (whichever is less) to prune the logs.
|
||||
//
|
||||
// First there is a prep phase where we discover the reader region lock that
|
||||
// acts as a backstop to any pruning activity to stop there and go no further.
|
||||
//
|
||||
// There are three major pruning loops that follow. All expire from the oldest
|
||||
// entries. Since there are multiple log buffers, the Android logging facility
|
||||
// will appear to drop entries 'in the middle' when looking at multiple log
|
||||
// sources and buffers. This effect is slightly more prominent when we prune
|
||||
// the worst offender by logging source. Thus the logs slowly loose content
|
||||
// and value as you move back in time. This is preferred since chatty sources
|
||||
// invariably move the logs value down faster as less chatty sources would be
|
||||
// expired in the noise.
|
||||
//
|
||||
// The first pass prunes elements that match 3 possible rules:
|
||||
// 1) A high priority prune rule, for example ~100/20, which indicates elements from UID 100 and PID
|
||||
// 20 should be pruned in this first pass.
|
||||
// 2) The default chatty pruning rule, ~!. This rule sums the total size spent on log messages for
|
||||
// each UID this log buffer. If the highest sum consumes more than 12.5% of the log buffer, then
|
||||
// these elements from that UID are pruned.
|
||||
// 3) The default AID_SYSTEM pruning rule, ~1000/!. This rule is a special case to 2), if
|
||||
// AID_SYSTEM is the top consumer of the log buffer, then this rule sums the total size spent on
|
||||
// log messages for each PID in AID_SYSTEM in this log buffer and prunes elements from the PID
|
||||
// with the highest sum.
|
||||
// This pass reevaluates the sums for rules 2) and 3) for every log message pruned. It creates
|
||||
// 'chatty' entries for the elements that it prunes and merges related chatty entries together. It
|
||||
// completes when one of three conditions have been met:
|
||||
// 1) The requested element count has been pruned.
|
||||
// 2) There are no elements that match any of these rules.
|
||||
// 3) A reader is referencing the oldest element that would match these rules.
|
||||
//
|
||||
// The second pass prunes elements starting from the beginning of the log. It skips elements that
|
||||
// match any low priority prune rules. It completes when one of three conditions have been met:
|
||||
// 1) The requested element count has been pruned.
|
||||
// 2) All elements except those mwatching low priority prune rules have been pruned.
|
||||
// 3) A reader is referencing the oldest element that would match these rules.
|
||||
//
|
||||
// The final pass only happens if there are any low priority prune rules and if the first two passes
|
||||
// were unable to prune the requested number of elements. It prunes elements all starting from the
|
||||
// beginning of the log, regardless of if they match any low priority prune rules.
|
||||
//
|
||||
// If the requested number of logs was unable to be pruned, KickReader() is called to mitigate the
|
||||
// situation before the next call to Prune() and the function returns false. Otherwise, if the
|
||||
// requested number of logs or all logs present in the buffer are pruned, in the case of Clear(),
|
||||
// it returns true.
|
||||
bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
|
||||
LogReaderThread* oldest = nullptr;
|
||||
bool clearAll = pruneRows == ULONG_MAX;
|
||||
|
||||
// Region locked?
|
||||
for (const auto& reader_thread : reader_list()->reader_threads()) {
|
||||
if (!reader_thread->IsWatching(id)) {
|
||||
continue;
|
||||
}
|
||||
if (!oldest || oldest->start() > reader_thread->start() ||
|
||||
(oldest->start() == reader_thread->start() &&
|
||||
reader_thread->deadline().time_since_epoch().count() != 0)) {
|
||||
oldest = reader_thread.get();
|
||||
}
|
||||
}
|
||||
|
||||
LogBufferElementCollection::iterator it;
|
||||
|
||||
if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
|
||||
// Only here if clear all request from non system source, so chatty
|
||||
// filter logistics is not required.
|
||||
it = GetOldest(id);
|
||||
while (it != logs().end()) {
|
||||
LogBufferElement& element = *it;
|
||||
|
||||
if (element.log_id() != id || element.uid() != caller_uid) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldest && oldest->start() <= element.sequence()) {
|
||||
KickReader(oldest, id, pruneRows);
|
||||
return false;
|
||||
}
|
||||
|
||||
it = Erase(it);
|
||||
if (--pruneRows == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// First prune pass.
|
||||
bool check_high_priority = id != LOG_ID_SECURITY && prune_->HasHighPriorityPruneRules();
|
||||
while (!clearAll && (pruneRows > 0)) {
|
||||
// recalculate the worst offender on every batched pass
|
||||
int worst = -1; // not valid for uid() or getKey()
|
||||
size_t worst_sizes = 0;
|
||||
size_t second_worst_sizes = 0;
|
||||
pid_t worstPid = 0; // POSIX guarantees PID != 0
|
||||
|
||||
if (worstUidEnabledForLogid(id) && prune_->worst_uid_enabled()) {
|
||||
// Calculate threshold as 12.5% of available storage
|
||||
size_t threshold = max_size(id) / 8;
|
||||
|
||||
if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
|
||||
stats()->WorstTwoTags(threshold, &worst, &worst_sizes, &second_worst_sizes);
|
||||
// per-pid filter for AID_SYSTEM sources is too complex
|
||||
} else {
|
||||
stats()->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
|
||||
|
||||
if (worst == AID_SYSTEM && prune_->worst_pid_of_system_enabled()) {
|
||||
stats()->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// skip if we have neither a worst UID or high priority prune rules
|
||||
if (worst == -1 && !check_high_priority) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool kick = false;
|
||||
bool leading = true; // true if starting from the oldest log entry, false if starting from
|
||||
// a specific chatty entry.
|
||||
// Perform at least one mandatory garbage collection cycle in following
|
||||
// - clear leading chatty tags
|
||||
// - coalesce chatty tags
|
||||
// - check age-out of preserved logs
|
||||
bool gc = pruneRows <= 1;
|
||||
if (!gc && (worst != -1)) {
|
||||
{ // begin scope for worst found iterator
|
||||
LogBufferIteratorMap::iterator found = mLastWorst[id].find(worst);
|
||||
if (found != mLastWorst[id].end() && found->second != logs().end()) {
|
||||
leading = false;
|
||||
it = found->second;
|
||||
}
|
||||
}
|
||||
if (worstPid) { // begin scope for pid worst found iterator
|
||||
// FYI: worstPid only set if !LOG_ID_EVENTS and
|
||||
// !LOG_ID_SECURITY, not going to make that assumption ...
|
||||
LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(worstPid);
|
||||
if (found != mLastWorstPidOfSystem[id].end() && found->second != logs().end()) {
|
||||
leading = false;
|
||||
it = found->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (leading) {
|
||||
it = GetOldest(id);
|
||||
}
|
||||
static const log_time too_old{EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
|
||||
LogBufferElementCollection::iterator lastt;
|
||||
lastt = logs().end();
|
||||
--lastt;
|
||||
LogBufferElementLast last;
|
||||
while (it != logs().end()) {
|
||||
LogBufferElement& element = *it;
|
||||
|
||||
if (oldest && oldest->start() <= element.sequence()) {
|
||||
// Do not let chatty eliding trigger any reader mitigation
|
||||
break;
|
||||
}
|
||||
|
||||
if (element.log_id() != id) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
// below this point element->log_id() == id
|
||||
|
||||
uint16_t dropped = element.dropped_count();
|
||||
|
||||
// remove any leading drops
|
||||
if (leading && dropped) {
|
||||
it = Erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dropped && last.coalesce(&element, dropped)) {
|
||||
it = Erase(it, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.GetTag()
|
||||
: element.uid();
|
||||
|
||||
if (check_high_priority && prune_->IsHighPriority(&element)) {
|
||||
last.clear(&element);
|
||||
it = Erase(it);
|
||||
if (dropped) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pruneRows--;
|
||||
if (pruneRows == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (key == worst) {
|
||||
kick = true;
|
||||
if (worst_sizes < second_worst_sizes) {
|
||||
break;
|
||||
}
|
||||
worst_sizes -= element.msg_len();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.realtime() < (lastt->realtime() - too_old) ||
|
||||
element.realtime() > lastt->realtime()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (dropped) {
|
||||
last.add(&element);
|
||||
if (worstPid && ((!gc && element.pid() == worstPid) ||
|
||||
mLastWorstPidOfSystem[id].find(element.pid()) ==
|
||||
mLastWorstPidOfSystem[id].end())) {
|
||||
// element->uid() may not be AID_SYSTEM, next best
|
||||
// watermark if current one empty. id is not LOG_ID_EVENTS
|
||||
// or LOG_ID_SECURITY because of worstPid check.
|
||||
mLastWorstPidOfSystem[id][element.pid()] = it;
|
||||
}
|
||||
if ((!gc && !worstPid && (key == worst)) ||
|
||||
(mLastWorst[id].find(key) == mLastWorst[id].end())) {
|
||||
mLastWorst[id][key] = it;
|
||||
}
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key != worst || (worstPid && element.pid() != worstPid)) {
|
||||
leading = false;
|
||||
last.clear(&element);
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
// key == worst below here
|
||||
// If worstPid set, then element->pid() == worstPid below here
|
||||
|
||||
pruneRows--;
|
||||
if (pruneRows == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
kick = true;
|
||||
|
||||
uint16_t len = element.msg_len();
|
||||
|
||||
// do not create any leading drops
|
||||
if (leading) {
|
||||
it = Erase(it);
|
||||
} else {
|
||||
stats()->Drop(element.ToLogStatisticsElement());
|
||||
element.SetDropped(1);
|
||||
if (last.coalesce(&element, 1)) {
|
||||
it = Erase(it, true);
|
||||
} else {
|
||||
last.add(&element);
|
||||
if (worstPid && (!gc || mLastWorstPidOfSystem[id].find(worstPid) ==
|
||||
mLastWorstPidOfSystem[id].end())) {
|
||||
// element->uid() may not be AID_SYSTEM, next best
|
||||
// watermark if current one empty. id is not
|
||||
// LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
|
||||
mLastWorstPidOfSystem[id][worstPid] = it;
|
||||
}
|
||||
if ((!gc && !worstPid) || mLastWorst[id].find(worst) == mLastWorst[id].end()) {
|
||||
mLastWorst[id][worst] = it;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (worst_sizes < second_worst_sizes) {
|
||||
break;
|
||||
}
|
||||
worst_sizes -= len;
|
||||
}
|
||||
last.clear();
|
||||
|
||||
if (!kick || !prune_->worst_uid_enabled()) {
|
||||
break; // the following loop will ask bad clients to skip/drop
|
||||
}
|
||||
}
|
||||
|
||||
// Second prune pass.
|
||||
bool skipped_low_priority_prune = false;
|
||||
bool check_low_priority =
|
||||
id != LOG_ID_SECURITY && prune_->HasLowPriorityPruneRules() && !clearAll;
|
||||
it = GetOldest(id);
|
||||
while (pruneRows > 0 && it != logs().end()) {
|
||||
LogBufferElement& element = *it;
|
||||
|
||||
if (element.log_id() != id) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldest && oldest->start() <= element.sequence()) {
|
||||
if (!skipped_low_priority_prune) KickReader(oldest, id, pruneRows);
|
||||
break;
|
||||
}
|
||||
|
||||
if (check_low_priority && !element.dropped_count() && prune_->IsLowPriority(&element)) {
|
||||
skipped_low_priority_prune = true;
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
|
||||
it = Erase(it);
|
||||
pruneRows--;
|
||||
}
|
||||
|
||||
// Third prune pass.
|
||||
if (skipped_low_priority_prune && pruneRows > 0) {
|
||||
it = GetOldest(id);
|
||||
while (it != logs().end() && pruneRows > 0) {
|
||||
LogBufferElement& element = *it;
|
||||
|
||||
if (element.log_id() != id) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldest && oldest->start() <= element.sequence()) {
|
||||
KickReader(oldest, id, pruneRows);
|
||||
break;
|
||||
}
|
||||
|
||||
it = Erase(it);
|
||||
pruneRows--;
|
||||
}
|
||||
}
|
||||
|
||||
return pruneRows == 0 || it == logs().end();
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android/log.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
#include "LogBufferElement.h"
|
||||
#include "LogReaderList.h"
|
||||
#include "LogReaderThread.h"
|
||||
#include "LogStatistics.h"
|
||||
#include "LogTags.h"
|
||||
#include "LogWriter.h"
|
||||
#include "LogdLock.h"
|
||||
#include "PruneList.h"
|
||||
#include "SimpleLogBuffer.h"
|
||||
|
||||
typedef std::list<LogBufferElement> LogBufferElementCollection;
|
||||
|
||||
class ChattyLogBuffer : public SimpleLogBuffer {
|
||||
// watermark of any worst/chatty uid processing
|
||||
typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator> LogBufferIteratorMap;
|
||||
LogBufferIteratorMap mLastWorst[LOG_ID_MAX] GUARDED_BY(logd_lock);
|
||||
// watermark of any worst/chatty pid of system processing
|
||||
typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator> LogBufferPidIteratorMap;
|
||||
LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX] GUARDED_BY(logd_lock);
|
||||
|
||||
public:
|
||||
ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
|
||||
LogStatistics* stats);
|
||||
~ChattyLogBuffer();
|
||||
|
||||
protected:
|
||||
bool Prune(log_id_t id, unsigned long pruneRows, uid_t uid) REQUIRES(logd_lock) override;
|
||||
void LogInternal(LogBufferElement&& elem) REQUIRES(logd_lock) override;
|
||||
|
||||
private:
|
||||
LogBufferElementCollection::iterator Erase(LogBufferElementCollection::iterator it,
|
||||
bool coalesce = false) REQUIRES(logd_lock);
|
||||
|
||||
PruneList* prune_;
|
||||
|
||||
// This always contains a copy of the last message logged, for deduplication.
|
||||
std::optional<LogBufferElement> last_logged_elements_[LOG_ID_MAX] GUARDED_BY(logd_lock);
|
||||
// This contains an element if duplicate messages are seen.
|
||||
// Its `dropped` count is `duplicates seen - 1`.
|
||||
std::optional<LogBufferElement> duplicate_elements_[LOG_ID_MAX] GUARDED_BY(logd_lock);
|
||||
};
|
|
@ -1,348 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogBufferTest.h"
|
||||
|
||||
class ChattyLogBufferTest : public LogBufferTest {};
|
||||
|
||||
TEST_P(ChattyLogBufferTest, deduplication_simple) {
|
||||
auto make_message = [&](uint32_t sec, const char* tag, const char* msg,
|
||||
bool regex = false) -> LogMessage {
|
||||
logger_entry entry = {
|
||||
.pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_MAIN, .uid = 0};
|
||||
std::string message;
|
||||
message.push_back(ANDROID_LOG_INFO);
|
||||
message.append(tag);
|
||||
message.push_back('\0');
|
||||
message.append(msg);
|
||||
message.push_back('\0');
|
||||
return {entry, message, regex};
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
std::vector<LogMessage> log_messages = {
|
||||
make_message(0, "test_tag", "duplicate"),
|
||||
make_message(1, "test_tag", "duplicate"),
|
||||
make_message(2, "test_tag", "not_same"),
|
||||
make_message(3, "test_tag", "duplicate"),
|
||||
make_message(4, "test_tag", "duplicate"),
|
||||
make_message(5, "test_tag", "not_same"),
|
||||
make_message(6, "test_tag", "duplicate"),
|
||||
make_message(7, "test_tag", "duplicate"),
|
||||
make_message(8, "test_tag", "duplicate"),
|
||||
make_message(9, "test_tag", "not_same"),
|
||||
make_message(10, "test_tag", "duplicate"),
|
||||
make_message(11, "test_tag", "duplicate"),
|
||||
make_message(12, "test_tag", "duplicate"),
|
||||
make_message(13, "test_tag", "duplicate"),
|
||||
make_message(14, "test_tag", "duplicate"),
|
||||
make_message(15, "test_tag", "duplicate"),
|
||||
make_message(16, "test_tag", "not_same"),
|
||||
make_message(100, "test_tag", "duplicate"),
|
||||
make_message(200, "test_tag", "duplicate"),
|
||||
make_message(300, "test_tag", "duplicate"),
|
||||
};
|
||||
// clang-format on
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
|
||||
|
||||
std::unique_ptr<FlushToState> flush_to_state =
|
||||
log_buffer_->CreateFlushToState(1, kLogMaskAll);
|
||||
EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
|
||||
}
|
||||
|
||||
std::vector<LogMessage> expected_log_messages = {
|
||||
make_message(0, "test_tag", "duplicate"),
|
||||
make_message(1, "test_tag", "duplicate"),
|
||||
make_message(2, "test_tag", "not_same"),
|
||||
make_message(3, "test_tag", "duplicate"),
|
||||
make_message(4, "test_tag", "duplicate"),
|
||||
make_message(5, "test_tag", "not_same"),
|
||||
// 3 duplicate logs together print the first, a 1 count chatty message, then the last.
|
||||
make_message(6, "test_tag", "duplicate"),
|
||||
make_message(7, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ identical 1 line", true),
|
||||
make_message(8, "test_tag", "duplicate"),
|
||||
make_message(9, "test_tag", "not_same"),
|
||||
// 6 duplicate logs together print the first, a 4 count chatty message, then the last.
|
||||
make_message(10, "test_tag", "duplicate"),
|
||||
make_message(14, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ identical 4 lines", true),
|
||||
make_message(15, "test_tag", "duplicate"),
|
||||
make_message(16, "test_tag", "not_same"),
|
||||
// duplicate logs > 1 minute apart are not deduplicated.
|
||||
make_message(100, "test_tag", "duplicate"),
|
||||
make_message(200, "test_tag", "duplicate"),
|
||||
make_message(300, "test_tag", "duplicate"),
|
||||
};
|
||||
FixupMessages(&expected_log_messages);
|
||||
CompareLogMessages(expected_log_messages, read_log_messages);
|
||||
};
|
||||
|
||||
TEST_P(ChattyLogBufferTest, deduplication_overflow) {
|
||||
auto make_message = [&](uint32_t sec, const char* tag, const char* msg,
|
||||
bool regex = false) -> LogMessage {
|
||||
logger_entry entry = {
|
||||
.pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_MAIN, .uid = 0};
|
||||
std::string message;
|
||||
message.push_back(ANDROID_LOG_INFO);
|
||||
message.append(tag);
|
||||
message.push_back('\0');
|
||||
message.append(msg);
|
||||
message.push_back('\0');
|
||||
return {entry, message, regex};
|
||||
};
|
||||
|
||||
uint32_t sec = 0;
|
||||
std::vector<LogMessage> log_messages = {
|
||||
make_message(sec++, "test_tag", "normal"),
|
||||
};
|
||||
size_t expired_per_chatty_message = std::numeric_limits<uint16_t>::max();
|
||||
for (size_t i = 0; i < expired_per_chatty_message + 3; ++i) {
|
||||
log_messages.emplace_back(make_message(sec++, "test_tag", "duplicate"));
|
||||
}
|
||||
log_messages.emplace_back(make_message(sec++, "test_tag", "normal"));
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
|
||||
std::unique_ptr<FlushToState> flush_to_state =
|
||||
log_buffer_->CreateFlushToState(1, kLogMaskAll);
|
||||
EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
|
||||
}
|
||||
|
||||
std::vector<LogMessage> expected_log_messages = {
|
||||
make_message(0, "test_tag", "normal"),
|
||||
make_message(1, "test_tag", "duplicate"),
|
||||
make_message(expired_per_chatty_message + 1, "chatty",
|
||||
"uid=0\\([^\\)]+\\) [^ ]+ identical 65535 lines", true),
|
||||
make_message(expired_per_chatty_message + 2, "chatty",
|
||||
"uid=0\\([^\\)]+\\) [^ ]+ identical 1 line", true),
|
||||
make_message(expired_per_chatty_message + 3, "test_tag", "duplicate"),
|
||||
make_message(expired_per_chatty_message + 4, "test_tag", "normal"),
|
||||
};
|
||||
FixupMessages(&expected_log_messages);
|
||||
CompareLogMessages(expected_log_messages, read_log_messages);
|
||||
}
|
||||
|
||||
TEST_P(ChattyLogBufferTest, deduplication_liblog) {
|
||||
auto make_message = [&](uint32_t sec, int32_t tag, int32_t count) -> LogMessage {
|
||||
logger_entry entry = {
|
||||
.pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_EVENTS, .uid = 0};
|
||||
android_log_event_int_t liblog_event = {
|
||||
.header.tag = tag, .payload.type = EVENT_TYPE_INT, .payload.data = count};
|
||||
return {entry, std::string(reinterpret_cast<char*>(&liblog_event), sizeof(liblog_event)),
|
||||
false};
|
||||
};
|
||||
|
||||
// LIBLOG_LOG_TAG
|
||||
std::vector<LogMessage> log_messages = {
|
||||
make_message(0, 1234, 1),
|
||||
make_message(1, LIBLOG_LOG_TAG, 3),
|
||||
make_message(2, 1234, 2),
|
||||
make_message(3, LIBLOG_LOG_TAG, 3),
|
||||
make_message(4, LIBLOG_LOG_TAG, 4),
|
||||
make_message(5, 1234, 223),
|
||||
make_message(6, LIBLOG_LOG_TAG, 2),
|
||||
make_message(7, LIBLOG_LOG_TAG, 3),
|
||||
make_message(8, LIBLOG_LOG_TAG, 4),
|
||||
make_message(9, 1234, 227),
|
||||
make_message(10, LIBLOG_LOG_TAG, 1),
|
||||
make_message(11, LIBLOG_LOG_TAG, 3),
|
||||
make_message(12, LIBLOG_LOG_TAG, 2),
|
||||
make_message(13, LIBLOG_LOG_TAG, 3),
|
||||
make_message(14, LIBLOG_LOG_TAG, 5),
|
||||
make_message(15, 1234, 227),
|
||||
make_message(16, LIBLOG_LOG_TAG, 2),
|
||||
make_message(17, LIBLOG_LOG_TAG, std::numeric_limits<int32_t>::max()),
|
||||
make_message(18, LIBLOG_LOG_TAG, 3),
|
||||
make_message(19, LIBLOG_LOG_TAG, 5),
|
||||
make_message(20, 1234, 227),
|
||||
};
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
|
||||
std::unique_ptr<FlushToState> flush_to_state =
|
||||
log_buffer_->CreateFlushToState(1, kLogMaskAll);
|
||||
EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
|
||||
}
|
||||
|
||||
std::vector<LogMessage> expected_log_messages = {
|
||||
make_message(0, 1234, 1),
|
||||
make_message(1, LIBLOG_LOG_TAG, 3),
|
||||
make_message(2, 1234, 2),
|
||||
make_message(3, LIBLOG_LOG_TAG, 3),
|
||||
make_message(4, LIBLOG_LOG_TAG, 4),
|
||||
make_message(5, 1234, 223),
|
||||
// More than 2 liblog events (3 here), sum their value into the third message.
|
||||
make_message(6, LIBLOG_LOG_TAG, 2),
|
||||
make_message(8, LIBLOG_LOG_TAG, 7),
|
||||
make_message(9, 1234, 227),
|
||||
// More than 2 liblog events (5 here), sum their value into the third message.
|
||||
make_message(10, LIBLOG_LOG_TAG, 1),
|
||||
make_message(14, LIBLOG_LOG_TAG, 13),
|
||||
make_message(15, 1234, 227),
|
||||
// int32_t max is the max for a chatty message, beyond that we must use new messages.
|
||||
make_message(16, LIBLOG_LOG_TAG, 2),
|
||||
make_message(17, LIBLOG_LOG_TAG, std::numeric_limits<int32_t>::max()),
|
||||
make_message(19, LIBLOG_LOG_TAG, 8),
|
||||
make_message(20, 1234, 227),
|
||||
};
|
||||
FixupMessages(&expected_log_messages);
|
||||
CompareLogMessages(expected_log_messages, read_log_messages);
|
||||
};
|
||||
|
||||
TEST_P(ChattyLogBufferTest, no_leading_chatty_simple) {
|
||||
auto make_message = [&](uint32_t sec, int32_t pid, uint32_t uid, uint32_t lid, const char* tag,
|
||||
const char* msg, bool regex = false) -> LogMessage {
|
||||
logger_entry entry = {.pid = pid, .tid = 1, .sec = sec, .nsec = 1, .lid = lid, .uid = uid};
|
||||
std::string message;
|
||||
message.push_back(ANDROID_LOG_INFO);
|
||||
message.append(tag);
|
||||
message.push_back('\0');
|
||||
message.append(msg);
|
||||
message.push_back('\0');
|
||||
return {entry, message, regex};
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
std::vector<LogMessage> log_messages = {
|
||||
make_message(1, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
|
||||
make_message(2, 2, 2, LOG_ID_SYSTEM, "test_tag", "duplicate2"),
|
||||
make_message(3, 2, 2, LOG_ID_SYSTEM, "test_tag", "duplicate2"),
|
||||
make_message(4, 2, 2, LOG_ID_SYSTEM, "test_tag", "duplicate2"),
|
||||
make_message(6, 2, 2, LOG_ID_SYSTEM, "test_tag", "not duplicate2"),
|
||||
make_message(7, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
|
||||
make_message(8, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
|
||||
make_message(9, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
|
||||
make_message(10, 1, 1, LOG_ID_MAIN, "test_tag", "not duplicate1"),
|
||||
};
|
||||
// clang-format on
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
// After logging log_messages, the below is what should be in the buffer:
|
||||
// PID=1, LOG_ID_MAIN duplicate1
|
||||
// [1] PID=2, LOG_ID_SYSTEM duplicate2
|
||||
// PID=2, LOG_ID_SYSTEM chatty drop
|
||||
// PID=2, LOG_ID_SYSTEM duplicate2
|
||||
// PID=2, LOG_ID_SYSTEM not duplicate2
|
||||
// [2] PID=1, LOG_ID_MAIN chatty drop
|
||||
// [3] PID=1, LOG_ID_MAIN duplicate1
|
||||
// PID=1, LOG_ID_MAIN not duplicate1
|
||||
|
||||
// We then read from the 2nd sequence number, starting from log message [1], but filtering out
|
||||
// everything but PID=1, which results in us starting with log message [2], which is a chatty
|
||||
// drop. Code prior to this test case would erroneously print it. The intended behavior that
|
||||
// this test checks prints logs starting from log message [3].
|
||||
|
||||
// clang-format off
|
||||
std::vector<LogMessage> expected_log_messages = {
|
||||
make_message(9, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
|
||||
make_message(10, 1, 1, LOG_ID_MAIN, "test_tag", "not duplicate1"),
|
||||
};
|
||||
FixupMessages(&expected_log_messages);
|
||||
// clang-format on
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
bool released = false;
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
|
||||
std::unique_ptr<LogReaderThread> log_reader(
|
||||
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
|
||||
0, ~0, 1, {}, 2, {}));
|
||||
reader_list_.reader_threads().emplace_back(std::move(log_reader));
|
||||
}
|
||||
|
||||
while (!released) {
|
||||
usleep(5000);
|
||||
}
|
||||
|
||||
CompareLogMessages(expected_log_messages, read_log_messages);
|
||||
}
|
||||
|
||||
TEST_P(ChattyLogBufferTest, no_leading_chatty_tail) {
|
||||
auto make_message = [&](uint32_t sec, const char* tag, const char* msg,
|
||||
bool regex = false) -> LogMessage {
|
||||
logger_entry entry = {
|
||||
.pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_MAIN, .uid = 0};
|
||||
std::string message;
|
||||
message.push_back(ANDROID_LOG_INFO);
|
||||
message.append(tag);
|
||||
message.push_back('\0');
|
||||
message.append(msg);
|
||||
message.push_back('\0');
|
||||
return {entry, message, regex};
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
std::vector<LogMessage> log_messages = {
|
||||
make_message(1, "test_tag", "duplicate"),
|
||||
make_message(2, "test_tag", "duplicate"),
|
||||
make_message(3, "test_tag", "duplicate"),
|
||||
make_message(4, "test_tag", "not_duplicate"),
|
||||
};
|
||||
// clang-format on
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
// After logging log_messages, the below is what should be in the buffer:
|
||||
// "duplicate"
|
||||
// chatty
|
||||
// "duplicate"
|
||||
// "not duplicate"
|
||||
|
||||
// We then read the tail 3 messages expecting there to not be a chatty message, meaning that we
|
||||
// should only see the last two messages.
|
||||
|
||||
// clang-format off
|
||||
std::vector<LogMessage> expected_log_messages = {
|
||||
make_message(3, "test_tag", "duplicate"),
|
||||
make_message(4, "test_tag", "not_duplicate"),
|
||||
};
|
||||
FixupMessages(&expected_log_messages);
|
||||
// clang-format on
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
bool released = false;
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
|
||||
std::unique_ptr<LogReaderThread> log_reader(
|
||||
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
|
||||
3, ~0, 0, {}, 1, {}));
|
||||
reader_list_.reader_threads().emplace_back(std::move(log_reader));
|
||||
}
|
||||
|
||||
while (!released) {
|
||||
usleep(5000);
|
||||
}
|
||||
|
||||
CompareLogMessages(expected_log_messages, read_log_messages);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ChattyLogBufferTests, ChattyLogBufferTest, testing::Values("chatty"));
|
|
@ -1,309 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "CommandListener.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <log/log_properties.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <sysutils/SocketClient.h>
|
||||
|
||||
#include "LogPermissions.h"
|
||||
|
||||
CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune,
|
||||
LogStatistics* stats)
|
||||
: FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune), stats_(stats) {
|
||||
registerCmd(new ClearCmd(this));
|
||||
registerCmd(new GetBufSizeCmd(this));
|
||||
registerCmd(new SetBufSizeCmd(this));
|
||||
registerCmd(new GetBufSizeReadableCmd(this));
|
||||
registerCmd(new GetBufSizeUsedCmd(this));
|
||||
registerCmd(new GetStatisticsCmd(this));
|
||||
registerCmd(new SetPruneListCmd(this));
|
||||
registerCmd(new GetPruneListCmd(this));
|
||||
registerCmd(new GetEventTagCmd(this));
|
||||
registerCmd(new ReinitCmd(this));
|
||||
registerCmd(new ExitCmd(this));
|
||||
}
|
||||
|
||||
static void setname() {
|
||||
static bool name_set;
|
||||
if (!name_set) {
|
||||
prctl(PR_SET_NAME, "logd.control");
|
||||
name_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static int LogIdCommand(SocketClient* cli, int argc, char** argv, F&& function) {
|
||||
setname();
|
||||
if (argc < 2) {
|
||||
cli->sendMsg("Missing Argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_id;
|
||||
if (!android::base::ParseInt(argv[1], &log_id, static_cast<int>(LOG_ID_MAIN),
|
||||
static_cast<int>(LOG_ID_KERNEL))) {
|
||||
cli->sendMsg("Range Error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
function(static_cast<log_id_t>(log_id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CommandListener::ClearCmd::runCommand(SocketClient* cli, int argc, char** argv) {
|
||||
uid_t uid = cli->getUid();
|
||||
if (clientHasLogCredentials(cli)) {
|
||||
uid = AID_ROOT;
|
||||
}
|
||||
|
||||
return LogIdCommand(cli, argc, argv, [&](log_id_t id) {
|
||||
cli->sendMsg(buf()->Clear(id, uid) ? "success" : "busy");
|
||||
});
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static int LogSizeCommand(SocketClient* cli, int argc, char** argv, F&& size_function) {
|
||||
return LogIdCommand(cli, argc, argv, [&](log_id_t log_id) {
|
||||
cli->sendMsg(std::to_string(size_function(log_id)).c_str());
|
||||
});
|
||||
}
|
||||
|
||||
int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc, char** argv) {
|
||||
return LogSizeCommand(cli, argc, argv, [this](log_id_t id) { return buf()->GetSize(id); });
|
||||
}
|
||||
|
||||
int CommandListener::GetBufSizeReadableCmd::runCommand(SocketClient* cli, int argc, char** argv) {
|
||||
return LogSizeCommand(cli, argc, argv,
|
||||
[this](log_id_t id) { return stats()->SizeReadable(id); });
|
||||
}
|
||||
|
||||
int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc, char** argv) {
|
||||
return LogSizeCommand(cli, argc, argv, [this](log_id_t id) { return stats()->Sizes(id); });
|
||||
}
|
||||
|
||||
int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
|
||||
char** argv) {
|
||||
if (!clientHasLogCredentials(cli)) {
|
||||
cli->sendMsg("Permission Denied");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc < 3) {
|
||||
cli->sendMsg("Missing Argument");
|
||||
return 0;
|
||||
}
|
||||
size_t size = atol(argv[2]);
|
||||
|
||||
return LogIdCommand(cli, argc, argv, [&](log_id_t log_id) {
|
||||
cli->sendMsg(buf()->SetSize(log_id, size) ? "success" : "busy");
|
||||
});
|
||||
}
|
||||
|
||||
// This returns a string with a length prefix with the format <length>\n<data>\n\f. The length
|
||||
// prefix includes the length of the prefix itself.
|
||||
static std::string PackageString(const std::string& str) {
|
||||
size_t overhead_length = 3; // \n \n \f.
|
||||
|
||||
// Number of digits needed to represent length(str + overhead_length).
|
||||
size_t str_size_digits = 1 + static_cast<size_t>(log10(str.size() + overhead_length));
|
||||
// Number of digits needed to represent the total size.
|
||||
size_t total_size_digits =
|
||||
1 + static_cast<size_t>(log10(str.size() + overhead_length + str_size_digits));
|
||||
|
||||
// If adding the size prefix causes a new digit to be required to represent the new total
|
||||
// size, add it to the 'overhead_length'. This can only happen once, since each new digit
|
||||
// allows for 10x the previous size to be recorded.
|
||||
if (total_size_digits != str_size_digits) {
|
||||
overhead_length++;
|
||||
}
|
||||
|
||||
size_t total_size = str.size() + overhead_length + str_size_digits;
|
||||
return android::base::StringPrintf("%zu\n%s\n\f", total_size, str.c_str());
|
||||
}
|
||||
|
||||
int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc, char** argv) {
|
||||
setname();
|
||||
uid_t uid = cli->getUid();
|
||||
if (clientHasLogCredentials(cli)) {
|
||||
uid = AID_ROOT;
|
||||
}
|
||||
|
||||
unsigned int logMask = -1;
|
||||
pid_t pid = 0;
|
||||
if (argc > 1) {
|
||||
logMask = 0;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
static const char _pid[] = "pid=";
|
||||
if (!strncmp(argv[i], _pid, sizeof(_pid) - 1)) {
|
||||
pid = atol(argv[i] + sizeof(_pid) - 1);
|
||||
if (pid == 0) {
|
||||
cli->sendMsg("PID Error");
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int id = atoi(argv[i]);
|
||||
if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
|
||||
cli->sendMsg("Range Error");
|
||||
return 0;
|
||||
}
|
||||
logMask |= 1 << id;
|
||||
}
|
||||
}
|
||||
|
||||
cli->sendMsg(PackageString(stats()->Format(uid, pid, logMask)).c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli, int, char**) {
|
||||
setname();
|
||||
cli->sendMsg(PackageString(prune()->Format()).c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc, char** argv) {
|
||||
setname();
|
||||
if (!clientHasLogCredentials(cli)) {
|
||||
cli->sendMsg("Permission Denied");
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string str;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (str.length()) {
|
||||
str += " ";
|
||||
}
|
||||
str += argv[i];
|
||||
}
|
||||
|
||||
if (!prune()->Init(str.c_str())) {
|
||||
cli->sendMsg("Invalid");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cli->sendMsg("success");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc, char** argv) {
|
||||
setname();
|
||||
uid_t uid = cli->getUid();
|
||||
if (clientHasLogCredentials(cli)) {
|
||||
uid = AID_ROOT;
|
||||
}
|
||||
|
||||
const char* name = nullptr;
|
||||
const char* format = nullptr;
|
||||
const char* id = nullptr;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
static const char _name[] = "name=";
|
||||
if (!strncmp(argv[i], _name, strlen(_name))) {
|
||||
name = argv[i] + strlen(_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
static const char _format[] = "format=";
|
||||
if (!strncmp(argv[i], _format, strlen(_format))) {
|
||||
format = argv[i] + strlen(_format);
|
||||
continue;
|
||||
}
|
||||
|
||||
static const char _id[] = "id=";
|
||||
if (!strncmp(argv[i], _id, strlen(_id))) {
|
||||
id = argv[i] + strlen(_id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (id) {
|
||||
if (format || name) {
|
||||
cli->sendMsg("can not mix id= with either format= or name=");
|
||||
return 0;
|
||||
}
|
||||
cli->sendMsg(PackageString(tags()->formatEntry(atoi(id), uid)).c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
cli->sendMsg(PackageString(tags()->formatGetEventTag(uid, name, format)).c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int, char**) {
|
||||
setname();
|
||||
|
||||
LOG(INFO) << "logd reinit";
|
||||
buf()->Init();
|
||||
prune()->Init(nullptr);
|
||||
|
||||
// This only works on userdebug and eng devices to re-read the
|
||||
// /data/misc/logd/event-log-tags file right after /data is mounted.
|
||||
// The operation is near to boot and should only happen once. There
|
||||
// are races associated with its use since it can trigger a Rebuild
|
||||
// of the file, but that is a can-not-happen since the file was not
|
||||
// read yet. More dangerous if called later, but if all is well it
|
||||
// should just skip over everything and not write any new entries.
|
||||
if (__android_log_is_debuggable()) {
|
||||
tags()->ReadFileEventLogTags(tags()->debug_event_log_tags);
|
||||
}
|
||||
|
||||
cli->sendMsg("success");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CommandListener::ExitCmd::runCommand(SocketClient* cli, int, char**) {
|
||||
setname();
|
||||
|
||||
cli->sendMsg("success");
|
||||
parent_->release(cli);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CommandListener::getLogSocket() {
|
||||
static const char socketName[] = "logd";
|
||||
int sock = android_get_control_socket(socketName);
|
||||
|
||||
if (sock < 0) {
|
||||
sock = socket_local_server(
|
||||
socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sysutils/FrameworkCommand.h>
|
||||
#include <sysutils/FrameworkListener.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
#include "LogListener.h"
|
||||
#include "LogStatistics.h"
|
||||
#include "LogTags.h"
|
||||
#include "PruneList.h"
|
||||
|
||||
class CommandListener : public FrameworkListener {
|
||||
public:
|
||||
CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune, LogStatistics* log_statistics);
|
||||
virtual ~CommandListener() {}
|
||||
|
||||
private:
|
||||
static int getLogSocket();
|
||||
|
||||
LogBuffer* buf_;
|
||||
LogTags* tags_;
|
||||
PruneList* prune_;
|
||||
LogStatistics* stats_;
|
||||
|
||||
#define LogCmd(name, command_string) \
|
||||
class name##Cmd : public FrameworkCommand { \
|
||||
public: \
|
||||
explicit name##Cmd(CommandListener* parent) \
|
||||
: FrameworkCommand(#command_string), parent_(parent) {} \
|
||||
virtual ~name##Cmd() {} \
|
||||
int runCommand(SocketClient* c, int argc, char** argv); \
|
||||
\
|
||||
private: \
|
||||
LogBuffer* buf() const { return parent_->buf_; } \
|
||||
LogTags* tags() const { return parent_->tags_; } \
|
||||
PruneList* prune() const { return parent_->prune_; } \
|
||||
LogStatistics* stats() const { return parent_->stats_; } \
|
||||
CommandListener* parent_; \
|
||||
}
|
||||
|
||||
LogCmd(Clear, clear);
|
||||
LogCmd(GetBufSize, getLogSize);
|
||||
LogCmd(SetBufSize, setLogSize);
|
||||
LogCmd(GetBufSizeReadable, getLogSizeReadable);
|
||||
LogCmd(GetBufSizeUsed, getLogSizeUsed);
|
||||
LogCmd(GetStatistics, getStatistics);
|
||||
LogCmd(GetPruneList, getPruneList);
|
||||
LogCmd(SetPruneList, setPruneList);
|
||||
LogCmd(GetEventTag, getEventTag);
|
||||
LogCmd(Reinit, reinit);
|
||||
LogCmd(Exit, EXIT);
|
||||
#undef LogCmd
|
||||
};
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "CompressionEngine.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <zlib.h>
|
||||
#include <zstd.h>
|
||||
|
||||
CompressionEngine& CompressionEngine::GetInstance() {
|
||||
static CompressionEngine* engine = new ZstdCompressionEngine();
|
||||
return *engine;
|
||||
}
|
||||
|
||||
bool ZlibCompressionEngine::Compress(SerializedData& in, size_t data_length, SerializedData& out) {
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
int ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
|
||||
if (ret != Z_OK) {
|
||||
LOG(FATAL) << "deflateInit() failed";
|
||||
}
|
||||
|
||||
CHECK_LE(data_length, in.size());
|
||||
CHECK_LE(in.size(), std::numeric_limits<uint32_t>::max());
|
||||
uint32_t deflate_bound = deflateBound(&strm, in.size());
|
||||
|
||||
out.Resize(deflate_bound);
|
||||
|
||||
strm.avail_in = data_length;
|
||||
strm.next_in = in.data();
|
||||
strm.avail_out = out.size();
|
||||
strm.next_out = out.data();
|
||||
ret = deflate(&strm, Z_FINISH);
|
||||
CHECK_EQ(ret, Z_STREAM_END);
|
||||
|
||||
uint32_t compressed_size = strm.total_out;
|
||||
deflateEnd(&strm);
|
||||
|
||||
out.Resize(compressed_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZlibCompressionEngine::Decompress(SerializedData& in, SerializedData& out) {
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = in.size();
|
||||
strm.next_in = in.data();
|
||||
strm.avail_out = out.size();
|
||||
strm.next_out = out.data();
|
||||
|
||||
inflateInit(&strm);
|
||||
int ret = inflate(&strm, Z_NO_FLUSH);
|
||||
|
||||
CHECK_EQ(strm.avail_in, 0U);
|
||||
CHECK_EQ(strm.avail_out, 0U);
|
||||
CHECK_EQ(ret, Z_STREAM_END);
|
||||
inflateEnd(&strm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZstdCompressionEngine::Compress(SerializedData& in, size_t data_length, SerializedData& out) {
|
||||
CHECK_LE(data_length, in.size());
|
||||
|
||||
size_t compress_bound = ZSTD_compressBound(data_length);
|
||||
out.Resize(compress_bound);
|
||||
|
||||
size_t out_size = ZSTD_compress(out.data(), out.size(), in.data(), data_length, 1);
|
||||
if (ZSTD_isError(out_size)) {
|
||||
LOG(FATAL) << "ZSTD_compress failed: " << ZSTD_getErrorName(out_size);
|
||||
}
|
||||
out.Resize(out_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZstdCompressionEngine::Decompress(SerializedData& in, SerializedData& out) {
|
||||
size_t result = ZSTD_decompress(out.data(), out.size(), in.data(), in.size());
|
||||
if (ZSTD_isError(result)) {
|
||||
LOG(FATAL) << "ZSTD_decompress failed: " << ZSTD_getErrorName(result);
|
||||
}
|
||||
CHECK_EQ(result, out.size());
|
||||
return true;
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "SerializedData.h"
|
||||
|
||||
class CompressionEngine {
|
||||
public:
|
||||
static CompressionEngine& GetInstance();
|
||||
|
||||
virtual ~CompressionEngine(){};
|
||||
|
||||
virtual bool Compress(SerializedData& in, size_t data_length, SerializedData& out) = 0;
|
||||
// Decompress the contents of `in` into `out`. `out.size()` must be set to the decompressed
|
||||
// size of the contents.
|
||||
virtual bool Decompress(SerializedData& in, SerializedData& out) = 0;
|
||||
};
|
||||
|
||||
class ZlibCompressionEngine : public CompressionEngine {
|
||||
public:
|
||||
bool Compress(SerializedData& in, size_t data_length, SerializedData& out) override;
|
||||
bool Decompress(SerializedData& in, SerializedData& out) override;
|
||||
};
|
||||
|
||||
class ZstdCompressionEngine : public CompressionEngine {
|
||||
public:
|
||||
bool Compress(SerializedData& in, size_t data_length, SerializedData& out) override;
|
||||
bool Decompress(SerializedData& in, SerializedData& out) override;
|
||||
};
|
|
@ -1,382 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogAudit.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "LogKlog.h"
|
||||
#include "LogUtils.h"
|
||||
#include "libaudit.h"
|
||||
|
||||
using android::base::GetBoolProperty;
|
||||
|
||||
#define KMSG_PRIORITY(PRI) \
|
||||
'<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
|
||||
'0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
|
||||
|
||||
LogAudit::LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats)
|
||||
: SocketListener(getLogSocket(), false),
|
||||
logbuf(buf),
|
||||
fdDmesg(fdDmesg),
|
||||
main(GetBoolProperty("ro.logd.auditd.main", true)),
|
||||
events(GetBoolProperty("ro.logd.auditd.events", true)),
|
||||
initialized(false),
|
||||
stats_(stats) {
|
||||
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
|
||||
'l',
|
||||
'o',
|
||||
'g',
|
||||
'd',
|
||||
'.',
|
||||
'a',
|
||||
'u',
|
||||
'd',
|
||||
'i',
|
||||
't',
|
||||
'd',
|
||||
':',
|
||||
' ',
|
||||
's',
|
||||
't',
|
||||
'a',
|
||||
'r',
|
||||
't',
|
||||
'\n' };
|
||||
write(fdDmesg, auditd_message, sizeof(auditd_message));
|
||||
}
|
||||
|
||||
bool LogAudit::onDataAvailable(SocketClient* cli) {
|
||||
if (!initialized) {
|
||||
prctl(PR_SET_NAME, "logd.auditd");
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
struct audit_message rep;
|
||||
|
||||
rep.nlh.nlmsg_type = 0;
|
||||
rep.nlh.nlmsg_len = 0;
|
||||
rep.data[0] = '\0';
|
||||
|
||||
if (audit_get_reply(cli->getSocket(), &rep, GET_REPLY_BLOCKING, 0) < 0) {
|
||||
SLOGE("Failed on audit_get_reply with error: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool hasMetadata(char* str, int str_len) {
|
||||
// need to check and see if str already contains bug metadata from
|
||||
// possibility of stuttering if log audit crashes and then reloads kernel
|
||||
// messages. Kernel denials that contain metadata will either end in
|
||||
// "b/[0-9]+$" or "b/[0-9]+ duplicate messages suppressed$" which will put
|
||||
// a '/' character at either 9 or 39 indices away from the end of the str.
|
||||
return str_len >= 39 &&
|
||||
(str[str_len - 9] == '/' || str[str_len - 39] == '/');
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> LogAudit::populateDenialMap() {
|
||||
std::ifstream bug_file("/vendor/etc/selinux/selinux_denial_metadata");
|
||||
std::string line;
|
||||
// allocate a map for the static map pointer in auditParse to keep track of,
|
||||
// this function only runs once
|
||||
std::map<std::string, std::string> denial_to_bug;
|
||||
if (bug_file.good()) {
|
||||
std::string scontext;
|
||||
std::string tcontext;
|
||||
std::string tclass;
|
||||
std::string bug_num;
|
||||
while (std::getline(bug_file, line)) {
|
||||
std::stringstream split_line(line);
|
||||
split_line >> scontext >> tcontext >> tclass >> bug_num;
|
||||
denial_to_bug.emplace(scontext + tcontext + tclass, bug_num);
|
||||
}
|
||||
}
|
||||
return denial_to_bug;
|
||||
}
|
||||
|
||||
std::string LogAudit::denialParse(const std::string& denial, char terminator,
|
||||
const std::string& search_term) {
|
||||
size_t start_index = denial.find(search_term);
|
||||
if (start_index != std::string::npos) {
|
||||
start_index += search_term.length();
|
||||
return denial.substr(
|
||||
start_index, denial.find(terminator, start_index) - start_index);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void LogAudit::auditParse(const std::string& string, uid_t uid,
|
||||
std::string* bug_num) {
|
||||
static std::map<std::string, std::string> denial_to_bug =
|
||||
populateDenialMap();
|
||||
std::string scontext = denialParse(string, ':', "scontext=u:object_r:");
|
||||
std::string tcontext = denialParse(string, ':', "tcontext=u:object_r:");
|
||||
std::string tclass = denialParse(string, ' ', "tclass=");
|
||||
if (scontext.empty()) {
|
||||
scontext = denialParse(string, ':', "scontext=u:r:");
|
||||
}
|
||||
if (tcontext.empty()) {
|
||||
tcontext = denialParse(string, ':', "tcontext=u:r:");
|
||||
}
|
||||
auto search = denial_to_bug.find(scontext + tcontext + tclass);
|
||||
if (search != denial_to_bug.end()) {
|
||||
bug_num->assign(" " + search->second);
|
||||
} else {
|
||||
bug_num->assign("");
|
||||
}
|
||||
|
||||
// Ensure the uid name is not null before passing it to the bug string.
|
||||
if (uid >= AID_APP_START && uid <= AID_APP_END) {
|
||||
char* uidname = android::uidToName(uid);
|
||||
if (uidname) {
|
||||
bug_num->append(" app=");
|
||||
bug_num->append(uidname);
|
||||
free(uidname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int LogAudit::logPrint(const char* fmt, ...) {
|
||||
if (fmt == nullptr) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
|
||||
char* str = nullptr;
|
||||
va_start(args, fmt);
|
||||
int rc = vasprintf(&str, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
char* cp;
|
||||
// Work around kernels missing
|
||||
// https://github.com/torvalds/linux/commit/b8f89caafeb55fba75b74bea25adc4e4cd91be67
|
||||
// Such kernels improperly add newlines inside audit messages.
|
||||
while ((cp = strchr(str, '\n'))) {
|
||||
*cp = ' ';
|
||||
}
|
||||
|
||||
while ((cp = strstr(str, " "))) {
|
||||
memmove(cp, cp + 1, strlen(cp + 1) + 1);
|
||||
}
|
||||
pid_t pid = getpid();
|
||||
pid_t tid = gettid();
|
||||
uid_t uid = AID_LOGD;
|
||||
static const char pid_str[] = " pid=";
|
||||
char* pidptr = strstr(str, pid_str);
|
||||
if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
|
||||
cp = pidptr + sizeof(pid_str) - 1;
|
||||
pid = 0;
|
||||
while (isdigit(*cp)) {
|
||||
pid = (pid * 10) + (*cp - '0');
|
||||
++cp;
|
||||
}
|
||||
tid = pid;
|
||||
uid = stats_->PidToUid(pid);
|
||||
memmove(pidptr, cp, strlen(cp) + 1);
|
||||
}
|
||||
|
||||
bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
|
||||
static std::string denial_metadata;
|
||||
if ((fdDmesg >= 0) && initialized) {
|
||||
struct iovec iov[4];
|
||||
static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
|
||||
static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
|
||||
static const char newline[] = "\n";
|
||||
|
||||
auditParse(str, uid, &denial_metadata);
|
||||
iov[0].iov_base = info ? const_cast<char*>(log_info) : const_cast<char*>(log_warning);
|
||||
iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
|
||||
iov[1].iov_base = str;
|
||||
iov[1].iov_len = strlen(str);
|
||||
iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
|
||||
iov[2].iov_len = denial_metadata.length();
|
||||
iov[3].iov_base = const_cast<char*>(newline);
|
||||
iov[3].iov_len = strlen(newline);
|
||||
|
||||
writev(fdDmesg, iov, arraysize(iov));
|
||||
}
|
||||
|
||||
if (!main && !events) {
|
||||
free(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_time now(log_time::EPOCH);
|
||||
|
||||
static const char audit_str[] = " audit(";
|
||||
char* timeptr = strstr(str, audit_str);
|
||||
if (timeptr && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
|
||||
(*cp == ':')) {
|
||||
memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
|
||||
memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
|
||||
} else {
|
||||
now = log_time(CLOCK_REALTIME);
|
||||
}
|
||||
|
||||
// log to events
|
||||
|
||||
size_t str_len = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
|
||||
if (((fdDmesg < 0) || !initialized) && !hasMetadata(str, str_len))
|
||||
auditParse(str, uid, &denial_metadata);
|
||||
str_len = (str_len + denial_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
|
||||
? str_len + denial_metadata.length()
|
||||
: LOGGER_ENTRY_MAX_PAYLOAD;
|
||||
size_t message_len = str_len + sizeof(android_log_event_string_t);
|
||||
|
||||
unsigned int notify = 0;
|
||||
|
||||
if (events) { // begin scope for event buffer
|
||||
uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
|
||||
|
||||
android_log_event_string_t* event =
|
||||
reinterpret_cast<android_log_event_string_t*>(buffer);
|
||||
event->header.tag = htole32(AUDITD_LOG_TAG);
|
||||
event->type = EVENT_TYPE_STRING;
|
||||
event->length = htole32(str_len);
|
||||
memcpy(event->data, str, str_len - denial_metadata.length());
|
||||
memcpy(event->data + str_len - denial_metadata.length(),
|
||||
denial_metadata.c_str(), denial_metadata.length());
|
||||
|
||||
rc = logbuf->Log(LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
|
||||
(message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
|
||||
if (rc >= 0) {
|
||||
notify |= 1 << LOG_ID_EVENTS;
|
||||
}
|
||||
// end scope for event buffer
|
||||
}
|
||||
|
||||
// log to main
|
||||
|
||||
static const char comm_str[] = " comm=\"";
|
||||
const char* comm = strstr(str, comm_str);
|
||||
const char* estr = str + strlen(str);
|
||||
const char* commfree = nullptr;
|
||||
if (comm) {
|
||||
estr = comm;
|
||||
comm += sizeof(comm_str) - 1;
|
||||
} else if (pid == getpid()) {
|
||||
pid = tid;
|
||||
comm = "auditd";
|
||||
} else {
|
||||
comm = commfree = stats_->PidToName(pid);
|
||||
if (!comm) {
|
||||
comm = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
const char* ecomm = strchr(comm, '"');
|
||||
if (ecomm) {
|
||||
++ecomm;
|
||||
str_len = ecomm - comm;
|
||||
} else {
|
||||
str_len = strlen(comm) + 1;
|
||||
ecomm = "";
|
||||
}
|
||||
size_t prefix_len = estr - str;
|
||||
if (prefix_len > LOGGER_ENTRY_MAX_PAYLOAD) {
|
||||
prefix_len = LOGGER_ENTRY_MAX_PAYLOAD;
|
||||
}
|
||||
size_t suffix_len = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - prefix_len);
|
||||
message_len =
|
||||
str_len + prefix_len + suffix_len + denial_metadata.length() + 2;
|
||||
|
||||
if (main) { // begin scope for main buffer
|
||||
char newstr[message_len];
|
||||
|
||||
*newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
|
||||
strlcpy(newstr + 1, comm, str_len);
|
||||
strncpy(newstr + 1 + str_len, str, prefix_len);
|
||||
strncpy(newstr + 1 + str_len + prefix_len, ecomm, suffix_len);
|
||||
strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
|
||||
denial_metadata.c_str(), denial_metadata.length());
|
||||
|
||||
rc = logbuf->Log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
|
||||
(message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
|
||||
|
||||
if (rc >= 0) {
|
||||
notify |= 1 << LOG_ID_MAIN;
|
||||
}
|
||||
// end scope for main buffer
|
||||
}
|
||||
|
||||
free(const_cast<char*>(commfree));
|
||||
free(str);
|
||||
|
||||
if (notify) {
|
||||
if (rc < 0) {
|
||||
rc = message_len;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int LogAudit::log(char* buf, size_t len) {
|
||||
char* audit = strstr(buf, " audit(");
|
||||
if (!audit || (audit >= &buf[len])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*audit = '\0';
|
||||
|
||||
int rc;
|
||||
char* type = strstr(buf, "type=");
|
||||
if (type && (type < &buf[len])) {
|
||||
rc = logPrint("%s %s", type, audit + 1);
|
||||
} else {
|
||||
rc = logPrint("%s", audit + 1);
|
||||
}
|
||||
*audit = ' ';
|
||||
return rc;
|
||||
}
|
||||
|
||||
int LogAudit::getLogSocket() {
|
||||
int fd = audit_open();
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
if (audit_setup(fd, getpid()) < 0) {
|
||||
audit_close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <sysutils/SocketListener.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
#include "LogStatistics.h"
|
||||
|
||||
class LogAudit : public SocketListener {
|
||||
LogBuffer* logbuf;
|
||||
int fdDmesg; // fdDmesg >= 0 is functionally bool dmesg
|
||||
bool main;
|
||||
bool events;
|
||||
bool initialized;
|
||||
|
||||
public:
|
||||
LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats);
|
||||
int log(char* buf, size_t len);
|
||||
|
||||
protected:
|
||||
virtual bool onDataAvailable(SocketClient* cli);
|
||||
|
||||
private:
|
||||
static int getLogSocket();
|
||||
std::map<std::string, std::string> populateDenialMap();
|
||||
std::string denialParse(const std::string& denial, char terminator,
|
||||
const std::string& search_term);
|
||||
void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
|
||||
int logPrint(const char* fmt, ...)
|
||||
__attribute__((__format__(__printf__, 2, 3)));
|
||||
|
||||
LogStatistics* stats_;
|
||||
};
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <log/log.h>
|
||||
#include <log/log_read.h>
|
||||
|
||||
#include "LogWriter.h"
|
||||
#include "LogdLock.h"
|
||||
|
||||
// A mask to represent which log buffers a reader is watching, values are (1 << LOG_ID_MAIN), etc.
|
||||
using LogMask = uint32_t;
|
||||
constexpr uint32_t kLogMaskAll = 0xFFFFFFFF;
|
||||
|
||||
// State that a LogBuffer may want to persist across calls to FlushTo().
|
||||
class FlushToState {
|
||||
public:
|
||||
FlushToState(uint64_t start, LogMask log_mask) : start_(start), log_mask_(log_mask) {}
|
||||
virtual ~FlushToState() {}
|
||||
|
||||
uint64_t start() const { return start_; }
|
||||
void set_start(uint64_t start) { start_ = start; }
|
||||
|
||||
LogMask log_mask() const { return log_mask_; }
|
||||
|
||||
private:
|
||||
uint64_t start_;
|
||||
LogMask log_mask_;
|
||||
};
|
||||
|
||||
// Enum for the return values of the `filter` function passed to FlushTo().
|
||||
enum class FilterResult {
|
||||
kSkip,
|
||||
kStop,
|
||||
kWrite,
|
||||
};
|
||||
|
||||
class LogBuffer {
|
||||
public:
|
||||
virtual ~LogBuffer() {}
|
||||
|
||||
virtual void Init() = 0;
|
||||
|
||||
virtual int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
|
||||
const char* msg, uint16_t len) = 0;
|
||||
|
||||
virtual std::unique_ptr<FlushToState> CreateFlushToState(uint64_t start, LogMask log_mask)
|
||||
REQUIRES(logd_lock) = 0;
|
||||
virtual bool FlushTo(
|
||||
LogWriter* writer, FlushToState& state,
|
||||
const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
|
||||
log_time realtime)>& filter) REQUIRES(logd_lock) = 0;
|
||||
|
||||
virtual bool Clear(log_id_t id, uid_t uid) = 0;
|
||||
virtual size_t GetSize(log_id_t id) = 0;
|
||||
virtual bool SetSize(log_id_t id, size_t size) = 0;
|
||||
|
||||
virtual uint64_t sequence() const = 0;
|
||||
};
|
|
@ -1,297 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogBufferElement.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <log/log_read.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "LogStatistics.h"
|
||||
#include "LogUtils.h"
|
||||
|
||||
LogBufferElement::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)
|
||||
: uid_(uid),
|
||||
pid_(pid),
|
||||
tid_(tid),
|
||||
sequence_(sequence),
|
||||
realtime_(realtime),
|
||||
msg_len_(len),
|
||||
log_id_(log_id),
|
||||
dropped_(false) {
|
||||
msg_ = new char[len];
|
||||
memcpy(msg_, msg, len);
|
||||
}
|
||||
|
||||
LogBufferElement::LogBufferElement(const LogBufferElement& elem)
|
||||
: uid_(elem.uid_),
|
||||
pid_(elem.pid_),
|
||||
tid_(elem.tid_),
|
||||
sequence_(elem.sequence_),
|
||||
realtime_(elem.realtime_),
|
||||
msg_len_(elem.msg_len_),
|
||||
log_id_(elem.log_id_),
|
||||
dropped_(elem.dropped_) {
|
||||
if (dropped_) {
|
||||
tag_ = elem.GetTag();
|
||||
} else {
|
||||
msg_ = new char[msg_len_];
|
||||
memcpy(msg_, elem.msg_, msg_len_);
|
||||
}
|
||||
}
|
||||
|
||||
LogBufferElement::LogBufferElement(LogBufferElement&& elem) noexcept
|
||||
: uid_(elem.uid_),
|
||||
pid_(elem.pid_),
|
||||
tid_(elem.tid_),
|
||||
sequence_(elem.sequence_),
|
||||
realtime_(elem.realtime_),
|
||||
msg_len_(elem.msg_len_),
|
||||
log_id_(elem.log_id_),
|
||||
dropped_(elem.dropped_) {
|
||||
if (dropped_) {
|
||||
tag_ = elem.GetTag();
|
||||
} else {
|
||||
msg_ = elem.msg_;
|
||||
elem.msg_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
LogBufferElement::~LogBufferElement() {
|
||||
if (!dropped_) {
|
||||
delete[] msg_;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t LogBufferElement::GetTag() const {
|
||||
// Binary buffers have no tag.
|
||||
if (!IsBinary(log_id())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Dropped messages store the tag in place of msg_.
|
||||
if (dropped_) {
|
||||
return tag_;
|
||||
}
|
||||
|
||||
return MsgToTag(msg(), msg_len());
|
||||
}
|
||||
|
||||
LogStatisticsElement LogBufferElement::ToLogStatisticsElement() const {
|
||||
// Estimate the size of this element in the parent std::list<> by adding two void*'s
|
||||
// corresponding to the next/prev pointers and aligning to 64 bit.
|
||||
uint16_t element_in_list_size =
|
||||
(sizeof(*this) + 2 * sizeof(void*) + sizeof(uint64_t) - 1) & -sizeof(uint64_t);
|
||||
return LogStatisticsElement{
|
||||
.uid = uid(),
|
||||
.pid = pid(),
|
||||
.tid = tid(),
|
||||
.tag = GetTag(),
|
||||
.realtime = realtime(),
|
||||
.msg = msg(),
|
||||
.msg_len = msg_len(),
|
||||
.dropped_count = dropped_count(),
|
||||
.log_id = log_id(),
|
||||
.total_len = static_cast<uint16_t>(element_in_list_size + msg_len()),
|
||||
};
|
||||
}
|
||||
|
||||
uint16_t LogBufferElement::SetDropped(uint16_t value) {
|
||||
if (dropped_) {
|
||||
return dropped_count_ = value;
|
||||
}
|
||||
|
||||
// The tag information is saved in msg_ data, which is in a union with tag_, used after dropped_
|
||||
// is set to true. Therefore we save the tag value aside, delete msg_, then set tag_ to the tag
|
||||
// value in its place.
|
||||
auto old_tag = GetTag();
|
||||
delete[] msg_;
|
||||
msg_ = nullptr;
|
||||
|
||||
tag_ = old_tag;
|
||||
dropped_ = true;
|
||||
return dropped_count_ = value;
|
||||
}
|
||||
|
||||
// caller must own and free character string
|
||||
char* android::tidToName(pid_t tid) {
|
||||
char* retval = nullptr;
|
||||
char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
|
||||
int fd = open(buffer, O_RDONLY | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
ssize_t ret = read(fd, buffer, sizeof(buffer));
|
||||
if (ret >= (ssize_t)sizeof(buffer)) {
|
||||
ret = sizeof(buffer) - 1;
|
||||
}
|
||||
while ((ret > 0) && isspace(buffer[ret - 1])) {
|
||||
--ret;
|
||||
}
|
||||
if (ret > 0) {
|
||||
buffer[ret] = '\0';
|
||||
retval = strdup(buffer);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// if nothing for comm, check out cmdline
|
||||
char* name = android::pidToName(tid);
|
||||
if (!retval) {
|
||||
retval = name;
|
||||
name = nullptr;
|
||||
}
|
||||
|
||||
// check if comm is truncated, see if cmdline has full representation
|
||||
if (name) {
|
||||
// impossible for retval to be NULL if name not NULL
|
||||
size_t retval_len = strlen(retval);
|
||||
size_t name_len = strlen(name);
|
||||
// KISS: ToDo: Only checks prefix truncated, not suffix, or both
|
||||
if ((retval_len < name_len) &&
|
||||
!fastcmp<strcmp>(retval, name + name_len - retval_len)) {
|
||||
free(retval);
|
||||
retval = name;
|
||||
} else {
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
// assumption: msg_ == NULL
|
||||
size_t LogBufferElement::PopulateDroppedMessage(char*& buffer, LogStatistics* stats,
|
||||
bool lastSame) {
|
||||
static const char tag[] = "chatty";
|
||||
|
||||
if (!__android_log_is_loggable_len(ANDROID_LOG_INFO, tag, strlen(tag),
|
||||
ANDROID_LOG_VERBOSE)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char format_uid[] = "uid=%u%s%s %s %u line%s";
|
||||
const char* name = stats->UidToName(uid_);
|
||||
const char* commName = android::tidToName(tid_);
|
||||
if (!commName && (tid_ != pid_)) {
|
||||
commName = android::tidToName(pid_);
|
||||
}
|
||||
if (!commName) {
|
||||
commName = stats->PidToName(pid_);
|
||||
}
|
||||
if (name && name[0] && commName && (name[0] == commName[0])) {
|
||||
size_t len = strlen(name + 1);
|
||||
if (!strncmp(name + 1, commName + 1, len)) {
|
||||
if (commName[len + 1] == '\0') {
|
||||
free(const_cast<char*>(commName));
|
||||
commName = nullptr;
|
||||
} else {
|
||||
free(const_cast<char*>(name));
|
||||
name = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name) {
|
||||
char* buf = nullptr;
|
||||
int result = asprintf(&buf, "(%s)", name);
|
||||
if (result != -1) {
|
||||
free(const_cast<char*>(name));
|
||||
name = buf;
|
||||
}
|
||||
}
|
||||
if (commName) {
|
||||
char* buf = nullptr;
|
||||
int result = asprintf(&buf, " %s", commName);
|
||||
if (result != -1) {
|
||||
free(const_cast<char*>(commName));
|
||||
commName = buf;
|
||||
}
|
||||
}
|
||||
// identical to below to calculate the buffer size required
|
||||
const char* type = lastSame ? "identical" : "expire";
|
||||
size_t len = snprintf(nullptr, 0, format_uid, uid_, name ? name : "", commName ? commName : "",
|
||||
type, dropped_count(), (dropped_count() > 1) ? "s" : "");
|
||||
|
||||
size_t hdrLen;
|
||||
if (IsBinary(log_id())) {
|
||||
hdrLen = sizeof(android_log_event_string_t);
|
||||
} else {
|
||||
hdrLen = 1 + sizeof(tag);
|
||||
}
|
||||
|
||||
buffer = static_cast<char*>(calloc(1, hdrLen + len + 1));
|
||||
if (!buffer) {
|
||||
free(const_cast<char*>(name));
|
||||
free(const_cast<char*>(commName));
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t retval = hdrLen + len;
|
||||
if (IsBinary(log_id())) {
|
||||
android_log_event_string_t* event =
|
||||
reinterpret_cast<android_log_event_string_t*>(buffer);
|
||||
|
||||
event->header.tag = htole32(CHATTY_LOG_TAG);
|
||||
event->type = EVENT_TYPE_STRING;
|
||||
event->length = htole32(len);
|
||||
} else {
|
||||
++retval;
|
||||
buffer[0] = ANDROID_LOG_INFO;
|
||||
strcpy(buffer + 1, tag);
|
||||
}
|
||||
|
||||
snprintf(buffer + hdrLen, len + 1, format_uid, uid_, name ? name : "", commName ? commName : "",
|
||||
type, dropped_count(), (dropped_count() > 1) ? "s" : "");
|
||||
free(const_cast<char*>(name));
|
||||
free(const_cast<char*>(commName));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool LogBufferElement::FlushTo(LogWriter* writer, LogStatistics* stats, bool lastSame) {
|
||||
struct logger_entry entry = {};
|
||||
|
||||
entry.hdr_size = sizeof(struct logger_entry);
|
||||
entry.lid = log_id_;
|
||||
entry.pid = pid_;
|
||||
entry.tid = tid_;
|
||||
entry.uid = uid_;
|
||||
entry.sec = realtime_.tv_sec;
|
||||
entry.nsec = realtime_.tv_nsec;
|
||||
|
||||
char* buffer = nullptr;
|
||||
const char* msg;
|
||||
if (dropped_) {
|
||||
entry.len = PopulateDroppedMessage(buffer, stats, lastSame);
|
||||
if (!entry.len) return true;
|
||||
msg = buffer;
|
||||
} else {
|
||||
msg = msg_;
|
||||
entry.len = msg_len_;
|
||||
}
|
||||
|
||||
bool retval = writer->Write(entry, msg);
|
||||
|
||||
if (buffer) free(buffer);
|
||||
|
||||
return retval;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include "LogWriter.h"
|
||||
|
||||
#include "LogStatistics.h"
|
||||
|
||||
#define EXPIRE_HOUR_THRESHOLD 24 // Only expire chatty UID logs to preserve
|
||||
// non-chatty UIDs less than this age in hours
|
||||
#define EXPIRE_THRESHOLD 10 // A smaller expire count is considered too
|
||||
// chatty for the temporal expire messages
|
||||
#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
|
||||
|
||||
class __attribute__((packed)) LogBufferElement {
|
||||
public:
|
||||
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);
|
||||
LogBufferElement(const LogBufferElement& elem);
|
||||
LogBufferElement(LogBufferElement&& elem) noexcept;
|
||||
~LogBufferElement();
|
||||
|
||||
uint32_t GetTag() const;
|
||||
uint16_t SetDropped(uint16_t value);
|
||||
|
||||
bool FlushTo(LogWriter* writer, LogStatistics* parent, bool lastSame);
|
||||
|
||||
LogStatisticsElement ToLogStatisticsElement() const;
|
||||
|
||||
log_id_t log_id() const { return static_cast<log_id_t>(log_id_); }
|
||||
uid_t uid() const { return uid_; }
|
||||
pid_t pid() const { return pid_; }
|
||||
pid_t tid() const { return tid_; }
|
||||
uint16_t msg_len() const { return dropped_ ? 0 : msg_len_; }
|
||||
const char* msg() const { return dropped_ ? nullptr : msg_; }
|
||||
uint64_t sequence() const { return sequence_; }
|
||||
log_time realtime() const { return realtime_; }
|
||||
uint16_t dropped_count() const { return dropped_ ? dropped_count_ : 0; }
|
||||
|
||||
private:
|
||||
// assumption: mDropped == true
|
||||
size_t PopulateDroppedMessage(char*& buffer, LogStatistics* parent, bool lastSame);
|
||||
|
||||
// sized to match reality of incoming log packets
|
||||
const uint32_t uid_;
|
||||
const uint32_t pid_;
|
||||
const uint32_t tid_;
|
||||
uint64_t sequence_;
|
||||
log_time realtime_;
|
||||
union {
|
||||
char* msg_; // mDropped == false
|
||||
int32_t tag_; // mDropped == true
|
||||
};
|
||||
union {
|
||||
const uint16_t msg_len_; // mDropped == false
|
||||
uint16_t dropped_count_; // mDropped == true
|
||||
};
|
||||
const uint8_t log_id_;
|
||||
bool dropped_;
|
||||
};
|
|
@ -1,458 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogBufferTest.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
#include "LogReaderThread.h"
|
||||
#include "LogWriter.h"
|
||||
|
||||
using android::base::Join;
|
||||
using android::base::Split;
|
||||
using android::base::StringPrintf;
|
||||
|
||||
char* android::uidToName(uid_t) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static std::vector<std::string> CompareLoggerEntries(const logger_entry& expected,
|
||||
const logger_entry& result, bool ignore_len) {
|
||||
std::vector<std::string> errors;
|
||||
if (!ignore_len && expected.len != result.len) {
|
||||
errors.emplace_back(
|
||||
StringPrintf("len: expected %" PRIu16 " vs %" PRIu16, expected.len, result.len));
|
||||
}
|
||||
if (expected.hdr_size != result.hdr_size) {
|
||||
errors.emplace_back(StringPrintf("hdr_size: %" PRIu16 " vs %" PRIu16, expected.hdr_size,
|
||||
result.hdr_size));
|
||||
}
|
||||
if (expected.pid != result.pid) {
|
||||
errors.emplace_back(
|
||||
StringPrintf("pid: expected %" PRIi32 " vs %" PRIi32, expected.pid, result.pid));
|
||||
}
|
||||
if (expected.tid != result.tid) {
|
||||
errors.emplace_back(
|
||||
StringPrintf("tid: expected %" PRIu32 " vs %" PRIu32, expected.tid, result.tid));
|
||||
}
|
||||
if (expected.sec != result.sec) {
|
||||
errors.emplace_back(
|
||||
StringPrintf("sec: expected %" PRIu32 " vs %" PRIu32, expected.sec, result.sec));
|
||||
}
|
||||
if (expected.nsec != result.nsec) {
|
||||
errors.emplace_back(
|
||||
StringPrintf("nsec: expected %" PRIu32 " vs %" PRIu32, expected.nsec, result.nsec));
|
||||
}
|
||||
if (expected.lid != result.lid) {
|
||||
errors.emplace_back(
|
||||
StringPrintf("lid: expected %" PRIu32 " vs %" PRIu32, expected.lid, result.lid));
|
||||
}
|
||||
if (expected.uid != result.uid) {
|
||||
errors.emplace_back(
|
||||
StringPrintf("uid: expected %" PRIu32 " vs %" PRIu32, expected.uid, result.uid));
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
static std::string MakePrintable(std::string in) {
|
||||
if (in.size() > 80) {
|
||||
in = in.substr(0, 80) + "...";
|
||||
}
|
||||
std::string result;
|
||||
for (const char c : in) {
|
||||
if (isprint(c)) {
|
||||
result.push_back(c);
|
||||
} else {
|
||||
result.append(StringPrintf("\\%02x", static_cast<int>(c) & 0xFF));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string CompareMessages(const std::string& expected, const std::string& result) {
|
||||
if (expected == result) {
|
||||
return {};
|
||||
}
|
||||
size_t diff_index = 0;
|
||||
for (; diff_index < std::min(expected.size(), result.size()); ++diff_index) {
|
||||
if (expected[diff_index] != result[diff_index]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (diff_index < 80) {
|
||||
auto expected_short = MakePrintable(expected);
|
||||
auto result_short = MakePrintable(result);
|
||||
return StringPrintf("msg: expected '%s' vs '%s'", expected_short.c_str(),
|
||||
result_short.c_str());
|
||||
}
|
||||
|
||||
auto expected_short = MakePrintable(expected.substr(diff_index));
|
||||
auto result_short = MakePrintable(result.substr(diff_index));
|
||||
return StringPrintf("msg: index %zu: expected '%s' vs '%s'", diff_index, expected_short.c_str(),
|
||||
result_short.c_str());
|
||||
}
|
||||
|
||||
static std::string CompareRegexMessages(const std::string& expected, const std::string& result) {
|
||||
auto expected_pieces = Split(expected, std::string("\0", 1));
|
||||
auto result_pieces = Split(result, std::string("\0", 1));
|
||||
|
||||
if (expected_pieces.size() != 3 || result_pieces.size() != 3) {
|
||||
return StringPrintf(
|
||||
"msg: should have 3 null delimited strings found %d in expected, %d in result: "
|
||||
"'%s' vs '%s'",
|
||||
static_cast<int>(expected_pieces.size()), static_cast<int>(result_pieces.size()),
|
||||
MakePrintable(expected).c_str(), MakePrintable(result).c_str());
|
||||
}
|
||||
if (expected_pieces[0] != result_pieces[0]) {
|
||||
return StringPrintf("msg: tag/priority mismatch expected '%s' vs '%s'",
|
||||
MakePrintable(expected_pieces[0]).c_str(),
|
||||
MakePrintable(result_pieces[0]).c_str());
|
||||
}
|
||||
std::regex expected_tag_regex(expected_pieces[1]);
|
||||
if (!std::regex_search(result_pieces[1], expected_tag_regex)) {
|
||||
return StringPrintf("msg: message regex mismatch expected '%s' vs '%s'",
|
||||
MakePrintable(expected_pieces[1]).c_str(),
|
||||
MakePrintable(result_pieces[1]).c_str());
|
||||
}
|
||||
if (expected_pieces[2] != result_pieces[2]) {
|
||||
return StringPrintf("msg: nothing expected after final null character '%s' vs '%s'",
|
||||
MakePrintable(expected_pieces[2]).c_str(),
|
||||
MakePrintable(result_pieces[2]).c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void CompareLogMessages(const std::vector<LogMessage>& expected,
|
||||
const std::vector<LogMessage>& result) {
|
||||
EXPECT_EQ(expected.size(), result.size());
|
||||
size_t end = std::min(expected.size(), result.size());
|
||||
size_t num_errors = 0;
|
||||
for (size_t i = 0; i < end; ++i) {
|
||||
auto errors =
|
||||
CompareLoggerEntries(expected[i].entry, result[i].entry, expected[i].regex_compare);
|
||||
auto msg_error = expected[i].regex_compare
|
||||
? CompareRegexMessages(expected[i].message, result[i].message)
|
||||
: CompareMessages(expected[i].message, result[i].message);
|
||||
if (!msg_error.empty()) {
|
||||
errors.emplace_back(msg_error);
|
||||
}
|
||||
if (!errors.empty()) {
|
||||
GTEST_LOG_(ERROR) << "Mismatch log message " << i << "\n" << Join(errors, "\n");
|
||||
++num_errors;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(0U, num_errors);
|
||||
}
|
||||
|
||||
void FixupMessages(std::vector<LogMessage>* messages) {
|
||||
for (auto& [entry, message, _] : *messages) {
|
||||
entry.hdr_size = sizeof(logger_entry);
|
||||
entry.len = message.size();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(LogBufferTest, smoke) {
|
||||
std::vector<LogMessage> log_messages = {
|
||||
{{
|
||||
.pid = 1,
|
||||
.tid = 1,
|
||||
.sec = 1234,
|
||||
.nsec = 323001,
|
||||
.lid = LOG_ID_MAIN,
|
||||
.uid = 0,
|
||||
},
|
||||
"smoke test"},
|
||||
};
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
|
||||
std::unique_ptr<FlushToState> flush_to_state =
|
||||
log_buffer_->CreateFlushToState(1, kLogMaskAll);
|
||||
EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
|
||||
EXPECT_EQ(2ULL, flush_to_state->start());
|
||||
}
|
||||
CompareLogMessages(log_messages, read_log_messages);
|
||||
}
|
||||
|
||||
TEST_P(LogBufferTest, smoke_with_reader_thread) {
|
||||
std::vector<LogMessage> log_messages = {
|
||||
{{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"first"},
|
||||
{{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"second"},
|
||||
{{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_KERNEL, .uid = 0},
|
||||
"third"},
|
||||
{{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20004, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"fourth"},
|
||||
{{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20005, .lid = LOG_ID_RADIO, .uid = 0},
|
||||
"fifth"},
|
||||
{{.pid = 2, .tid = 2, .sec = 10000, .nsec = 20006, .lid = LOG_ID_RADIO, .uid = 0},
|
||||
"sixth"},
|
||||
{{.pid = 3, .tid = 2, .sec = 10000, .nsec = 20007, .lid = LOG_ID_RADIO, .uid = 0},
|
||||
"seventh"},
|
||||
{{.pid = 4, .tid = 2, .sec = 10000, .nsec = 20008, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"eighth"},
|
||||
{{.pid = 5, .tid = 2, .sec = 10000, .nsec = 20009, .lid = LOG_ID_CRASH, .uid = 0},
|
||||
"nineth"},
|
||||
{{.pid = 6, .tid = 2, .sec = 10000, .nsec = 20011, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"tenth"},
|
||||
};
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
bool released = false;
|
||||
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
|
||||
std::unique_ptr<LogReaderThread> log_reader(
|
||||
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
|
||||
0, kLogMaskAll, 0, {}, 1, {}));
|
||||
reader_list_.reader_threads().emplace_back(std::move(log_reader));
|
||||
}
|
||||
|
||||
while (!released) {
|
||||
usleep(5000);
|
||||
}
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
EXPECT_EQ(0U, reader_list_.reader_threads().size());
|
||||
}
|
||||
CompareLogMessages(log_messages, read_log_messages);
|
||||
}
|
||||
|
||||
// Generate random messages, set the 'sec' parameter explicit though, to be able to track the
|
||||
// expected order of messages.
|
||||
LogMessage GenerateRandomLogMessage(uint32_t sec) {
|
||||
auto rand_uint32 = [](int max) -> uint32_t { return rand() % max; };
|
||||
logger_entry entry = {
|
||||
.hdr_size = sizeof(logger_entry),
|
||||
.pid = rand() % 5000,
|
||||
.tid = rand_uint32(5000),
|
||||
.sec = sec,
|
||||
.nsec = rand_uint32(NS_PER_SEC),
|
||||
.lid = rand_uint32(LOG_ID_STATS),
|
||||
.uid = rand_uint32(100000),
|
||||
};
|
||||
|
||||
// See comment in ChattyLogBuffer::Log() for why this is disallowed.
|
||||
if (entry.nsec % 1000 == 0) {
|
||||
++entry.nsec;
|
||||
}
|
||||
|
||||
if (entry.lid == LOG_ID_EVENTS) {
|
||||
entry.lid = LOG_ID_KERNEL;
|
||||
}
|
||||
|
||||
std::string message;
|
||||
char priority = ANDROID_LOG_INFO + rand() % 2;
|
||||
message.push_back(priority);
|
||||
|
||||
int tag_length = 2 + rand() % 10;
|
||||
for (int i = 0; i < tag_length; ++i) {
|
||||
message.push_back('a' + rand() % 26);
|
||||
}
|
||||
message.push_back('\0');
|
||||
|
||||
int msg_length = 2 + rand() % 1000;
|
||||
for (int i = 0; i < msg_length; ++i) {
|
||||
message.push_back('a' + rand() % 26);
|
||||
}
|
||||
message.push_back('\0');
|
||||
|
||||
entry.len = message.size();
|
||||
|
||||
return {entry, message};
|
||||
}
|
||||
|
||||
TEST_P(LogBufferTest, random_messages) {
|
||||
srand(1);
|
||||
std::vector<LogMessage> log_messages;
|
||||
for (size_t i = 0; i < 1000; ++i) {
|
||||
log_messages.emplace_back(GenerateRandomLogMessage(i));
|
||||
}
|
||||
LogMessages(log_messages);
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
bool released = false;
|
||||
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
|
||||
std::unique_ptr<LogReaderThread> log_reader(
|
||||
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
|
||||
0, kLogMaskAll, 0, {}, 1, {}));
|
||||
reader_list_.reader_threads().emplace_back(std::move(log_reader));
|
||||
}
|
||||
|
||||
while (!released) {
|
||||
usleep(5000);
|
||||
}
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
EXPECT_EQ(0U, reader_list_.reader_threads().size());
|
||||
}
|
||||
CompareLogMessages(log_messages, read_log_messages);
|
||||
}
|
||||
|
||||
TEST_P(LogBufferTest, read_last_sequence) {
|
||||
std::vector<LogMessage> log_messages = {
|
||||
{{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"first"},
|
||||
{{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"second"},
|
||||
{{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"third"},
|
||||
};
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
bool released = false;
|
||||
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
|
||||
std::unique_ptr<LogReaderThread> log_reader(
|
||||
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
|
||||
0, kLogMaskAll, 0, {}, 3, {}));
|
||||
reader_list_.reader_threads().emplace_back(std::move(log_reader));
|
||||
}
|
||||
|
||||
while (!released) {
|
||||
usleep(5000);
|
||||
}
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
EXPECT_EQ(0U, reader_list_.reader_threads().size());
|
||||
}
|
||||
std::vector<LogMessage> expected_log_messages = {log_messages.back()};
|
||||
CompareLogMessages(expected_log_messages, read_log_messages);
|
||||
}
|
||||
|
||||
TEST_P(LogBufferTest, clear_logs) {
|
||||
// Log 3 initial logs.
|
||||
std::vector<LogMessage> log_messages = {
|
||||
{{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"first"},
|
||||
{{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"second"},
|
||||
{{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"third"},
|
||||
};
|
||||
FixupMessages(&log_messages);
|
||||
LogMessages(log_messages);
|
||||
|
||||
std::vector<LogMessage> read_log_messages;
|
||||
bool released = false;
|
||||
|
||||
// Connect a blocking reader.
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
|
||||
std::unique_ptr<LogReaderThread> log_reader(
|
||||
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), false,
|
||||
0, kLogMaskAll, 0, {}, 1, {}));
|
||||
reader_list_.reader_threads().emplace_back(std::move(log_reader));
|
||||
}
|
||||
|
||||
// Wait up to 250ms for the reader to read the first 3 logs.
|
||||
constexpr int kMaxRetryCount = 50;
|
||||
int count = 0;
|
||||
for (; count < kMaxRetryCount; ++count) {
|
||||
usleep(5000);
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
if (reader_list_.reader_threads().back()->start() == 4) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_LT(count, kMaxRetryCount);
|
||||
|
||||
// Clear the log buffer.
|
||||
log_buffer_->Clear(LOG_ID_MAIN, 0);
|
||||
|
||||
// Log 3 more logs.
|
||||
std::vector<LogMessage> after_clear_messages = {
|
||||
{{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"4th"},
|
||||
{{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"5th"},
|
||||
{{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_MAIN, .uid = 0},
|
||||
"6th"},
|
||||
};
|
||||
FixupMessages(&after_clear_messages);
|
||||
LogMessages(after_clear_messages);
|
||||
|
||||
// Wait up to 250ms for the reader to read the 3 additional logs.
|
||||
for (count = 0; count < kMaxRetryCount; ++count) {
|
||||
usleep(5000);
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
if (reader_list_.reader_threads().back()->start() == 7) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_LT(count, kMaxRetryCount);
|
||||
|
||||
// Release the reader, wait for it to get the signal then check that it has been deleted.
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
reader_list_.reader_threads().back()->Release();
|
||||
}
|
||||
while (!released) {
|
||||
usleep(5000);
|
||||
}
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
EXPECT_EQ(0U, reader_list_.reader_threads().size());
|
||||
}
|
||||
|
||||
// Check that we have read all 6 messages.
|
||||
std::vector<LogMessage> expected_log_messages = log_messages;
|
||||
expected_log_messages.insert(expected_log_messages.end(), after_clear_messages.begin(),
|
||||
after_clear_messages.end());
|
||||
CompareLogMessages(expected_log_messages, read_log_messages);
|
||||
|
||||
// Finally, call FlushTo and ensure that only the 3 logs after the clear remain in the buffer.
|
||||
std::vector<LogMessage> read_log_messages_after_clear;
|
||||
{
|
||||
auto lock = std::lock_guard{logd_lock};
|
||||
std::unique_ptr<LogWriter> test_writer(
|
||||
new TestWriter(&read_log_messages_after_clear, nullptr));
|
||||
std::unique_ptr<FlushToState> flush_to_state =
|
||||
log_buffer_->CreateFlushToState(1, kLogMaskAll);
|
||||
EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
|
||||
EXPECT_EQ(7ULL, flush_to_state->start());
|
||||
}
|
||||
CompareLogMessages(after_clear_messages, read_log_messages_after_clear);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(LogBufferTests, LogBufferTest,
|
||||
testing::Values("chatty", "serialized", "simple"));
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ChattyLogBuffer.h"
|
||||
#include "LogReaderList.h"
|
||||
#include "LogStatistics.h"
|
||||
#include "LogTags.h"
|
||||
#include "PruneList.h"
|
||||
#include "SerializedLogBuffer.h"
|
||||
#include "SimpleLogBuffer.h"
|
||||
|
||||
struct LogMessage {
|
||||
logger_entry entry;
|
||||
std::string message;
|
||||
bool regex_compare = false; // Only set for expected messages, when true 'message' should be
|
||||
// interpretted as a regex.
|
||||
};
|
||||
|
||||
// Compares the ordered list of expected and result, causing a test failure with appropriate
|
||||
// information on failure.
|
||||
void CompareLogMessages(const std::vector<LogMessage>& expected,
|
||||
const std::vector<LogMessage>& result);
|
||||
// Sets hdr_size and len parameters appropriately.
|
||||
void FixupMessages(std::vector<LogMessage>* messages);
|
||||
|
||||
class TestWriter : public LogWriter {
|
||||
public:
|
||||
TestWriter(std::vector<LogMessage>* msgs, bool* released)
|
||||
: LogWriter(0, true), msgs_(msgs), released_(released) {}
|
||||
bool Write(const logger_entry& entry, const char* message) override {
|
||||
msgs_->emplace_back(LogMessage{entry, std::string(message, entry.len), false});
|
||||
return true;
|
||||
}
|
||||
|
||||
void Release() {
|
||||
if (released_) *released_ = true;
|
||||
}
|
||||
|
||||
std::string name() const override { return "test_writer"; }
|
||||
|
||||
private:
|
||||
std::vector<LogMessage>* msgs_;
|
||||
bool* released_;
|
||||
};
|
||||
|
||||
class LogBufferTest : public testing::TestWithParam<std::string> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
if (GetParam() == "chatty") {
|
||||
log_buffer_.reset(new ChattyLogBuffer(&reader_list_, &tags_, &prune_, &stats_));
|
||||
} else if (GetParam() == "serialized") {
|
||||
log_buffer_.reset(new SerializedLogBuffer(&reader_list_, &tags_, &stats_));
|
||||
} else if (GetParam() == "simple") {
|
||||
log_buffer_.reset(new SimpleLogBuffer(&reader_list_, &tags_, &stats_));
|
||||
} else {
|
||||
FAIL() << "Unknown buffer type selected for test";
|
||||
}
|
||||
|
||||
log_id_for_each(i) { log_buffer_->SetSize(i, 1024 * 1024); }
|
||||
}
|
||||
|
||||
void LogMessages(const std::vector<LogMessage>& messages) {
|
||||
for (auto& [entry, message, _] : messages) {
|
||||
log_buffer_->Log(static_cast<log_id_t>(entry.lid), log_time(entry.sec, entry.nsec),
|
||||
entry.uid, entry.pid, entry.tid, message.c_str(), message.size());
|
||||
}
|
||||
}
|
||||
|
||||
LogReaderList reader_list_;
|
||||
LogTags tags_;
|
||||
PruneList prune_;
|
||||
LogStatistics stats_{false, true};
|
||||
std::unique_ptr<LogBuffer> log_buffer_;
|
||||
};
|
772
logd/LogKlog.cpp
772
logd/LogKlog.cpp
|
@ -1,772 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogKlog.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
|
||||
#define KMSG_PRIORITY(PRI) \
|
||||
'<', '0' + (LOG_SYSLOG | (PRI)) / 10, '0' + (LOG_SYSLOG | (PRI)) % 10, '>'
|
||||
|
||||
static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' };
|
||||
|
||||
// List of the _only_ needles we supply here to android::strnstr
|
||||
static const char suspendStr[] = "PM: suspend entry ";
|
||||
static const char resumeStr[] = "PM: suspend exit ";
|
||||
static const char suspendedStr[] = "Suspended for ";
|
||||
static const char healthdStr[] = "healthd";
|
||||
static const char batteryStr[] = ": battery ";
|
||||
static const char auditStr[] = " audit(";
|
||||
static const char klogdStr[] = "logd.klogd: ";
|
||||
|
||||
// Parsing is hard
|
||||
|
||||
// called if we see a '<', s is the next character, returns pointer after '>'
|
||||
static char* is_prio(char* s, ssize_t len) {
|
||||
if ((len <= 0) || !isdigit(*s++)) return nullptr;
|
||||
--len;
|
||||
static const size_t max_prio_len = (len < 4) ? len : 4;
|
||||
size_t priolen = 0;
|
||||
char c;
|
||||
while (((c = *s++)) && (++priolen <= max_prio_len)) {
|
||||
if (!isdigit(c)) return ((c == '>') && (*s == '[')) ? s : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// called if we see a '[', s is the next character, returns pointer after ']'
|
||||
static char* is_timestamp(char* s, ssize_t len) {
|
||||
while ((len > 0) && (*s == ' ')) {
|
||||
++s;
|
||||
--len;
|
||||
}
|
||||
if ((len <= 0) || !isdigit(*s++)) return nullptr;
|
||||
--len;
|
||||
bool first_period = true;
|
||||
char c;
|
||||
while ((len > 0) && ((c = *s++))) {
|
||||
--len;
|
||||
if ((c == '.') && first_period) {
|
||||
first_period = false;
|
||||
} else if (!isdigit(c)) {
|
||||
return ((c == ']') && !first_period && (*s == ' ')) ? s : nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Like strtok_r with "\r\n" except that we look for log signatures (regex)
|
||||
// \(\(<[0-9]\{1,4\}>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[]
|
||||
// *[0-9]+[.][0-9]+[]] \)
|
||||
// and split if we see a second one without a newline.
|
||||
// We allow nuls in content, monitoring the overall length and sub-length of
|
||||
// the discovered tokens.
|
||||
|
||||
#define SIGNATURE_MASK 0xF0
|
||||
// <digit> following ('0' to '9' masked with ~SIGNATURE_MASK) added to signature
|
||||
#define LESS_THAN_SIG SIGNATURE_MASK
|
||||
#define OPEN_BRACKET_SIG ((SIGNATURE_MASK << 1) & SIGNATURE_MASK)
|
||||
// space is one more than <digit> of 9
|
||||
#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10))
|
||||
|
||||
char* android::log_strntok_r(char* s, ssize_t& len, char*& last,
|
||||
ssize_t& sublen) {
|
||||
sublen = 0;
|
||||
if (len <= 0) return nullptr;
|
||||
if (!s) {
|
||||
if (!(s = last)) return nullptr;
|
||||
// fixup for log signature split <,
|
||||
// LESS_THAN_SIG + <digit>
|
||||
if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) {
|
||||
*s = (*s & ~SIGNATURE_MASK) + '0';
|
||||
*--s = '<';
|
||||
++len;
|
||||
}
|
||||
// fixup for log signature split [,
|
||||
// OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit>
|
||||
if ((*s & SIGNATURE_MASK) == OPEN_BRACKET_SIG) {
|
||||
*s = (*s == OPEN_BRACKET_SPACE) ? ' ' : (*s & ~SIGNATURE_MASK) + '0';
|
||||
*--s = '[';
|
||||
++len;
|
||||
}
|
||||
}
|
||||
|
||||
while ((len > 0) && ((*s == '\r') || (*s == '\n'))) {
|
||||
++s;
|
||||
--len;
|
||||
}
|
||||
|
||||
if (len <= 0) return last = nullptr;
|
||||
char *peek, *tok = s;
|
||||
|
||||
for (;;) {
|
||||
if (len <= 0) {
|
||||
last = nullptr;
|
||||
return tok;
|
||||
}
|
||||
char c = *s++;
|
||||
--len;
|
||||
ssize_t adjust;
|
||||
switch (c) {
|
||||
case '\r':
|
||||
case '\n':
|
||||
s[-1] = '\0';
|
||||
last = s;
|
||||
return tok;
|
||||
|
||||
case '<':
|
||||
peek = is_prio(s, len);
|
||||
if (!peek) break;
|
||||
if (s != (tok + 1)) { // not first?
|
||||
s[-1] = '\0';
|
||||
*s &= ~SIGNATURE_MASK;
|
||||
*s |= LESS_THAN_SIG; // signature for '<'
|
||||
last = s;
|
||||
return tok;
|
||||
}
|
||||
adjust = peek - s;
|
||||
if (adjust > len) {
|
||||
adjust = len;
|
||||
}
|
||||
sublen += adjust;
|
||||
len -= adjust;
|
||||
s = peek;
|
||||
if ((*s == '[') && ((peek = is_timestamp(s + 1, len - 1)))) {
|
||||
adjust = peek - s;
|
||||
if (adjust > len) {
|
||||
adjust = len;
|
||||
}
|
||||
sublen += adjust;
|
||||
len -= adjust;
|
||||
s = peek;
|
||||
}
|
||||
break;
|
||||
|
||||
case '[':
|
||||
peek = is_timestamp(s, len);
|
||||
if (!peek) break;
|
||||
if (s != (tok + 1)) { // not first?
|
||||
s[-1] = '\0';
|
||||
if (*s == ' ') {
|
||||
*s = OPEN_BRACKET_SPACE;
|
||||
} else {
|
||||
*s &= ~SIGNATURE_MASK;
|
||||
*s |= OPEN_BRACKET_SIG; // signature for '['
|
||||
}
|
||||
last = s;
|
||||
return tok;
|
||||
}
|
||||
adjust = peek - s;
|
||||
if (adjust > len) {
|
||||
adjust = len;
|
||||
}
|
||||
sublen += adjust;
|
||||
len -= adjust;
|
||||
s = peek;
|
||||
break;
|
||||
}
|
||||
++sublen;
|
||||
}
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
log_time LogKlog::correction = (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
|
||||
? log_time(log_time::EPOCH)
|
||||
: (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
|
||||
|
||||
LogKlog::LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats)
|
||||
: SocketListener(fdRead, false),
|
||||
logbuf(buf),
|
||||
signature(CLOCK_MONOTONIC),
|
||||
initialized(false),
|
||||
enableLogging(true),
|
||||
auditd(auditd),
|
||||
stats_(stats) {
|
||||
static const char klogd_message[] = "%s%s%" PRIu64 "\n";
|
||||
char buffer[strlen(priority_message) + strlen(klogdStr) +
|
||||
strlen(klogd_message) + 20];
|
||||
snprintf(buffer, sizeof(buffer), klogd_message, priority_message, klogdStr,
|
||||
signature.nsec());
|
||||
write(fdWrite, buffer, strlen(buffer));
|
||||
}
|
||||
|
||||
bool LogKlog::onDataAvailable(SocketClient* cli) {
|
||||
if (!initialized) {
|
||||
prctl(PR_SET_NAME, "logd.klogd");
|
||||
initialized = true;
|
||||
enableLogging = false;
|
||||
}
|
||||
|
||||
char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
|
||||
ssize_t len = 0;
|
||||
|
||||
for (;;) {
|
||||
ssize_t retval = 0;
|
||||
if (len < (ssize_t)(sizeof(buffer) - 1)) {
|
||||
retval =
|
||||
read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
|
||||
}
|
||||
if ((retval == 0) && (len <= 0)) {
|
||||
break;
|
||||
}
|
||||
if (retval < 0) {
|
||||
return false;
|
||||
}
|
||||
len += retval;
|
||||
bool full = len == (sizeof(buffer) - 1);
|
||||
char* ep = buffer + len;
|
||||
*ep = '\0';
|
||||
ssize_t sublen;
|
||||
for (char *ptr = nullptr, *tok = buffer;
|
||||
!!(tok = android::log_strntok_r(tok, len, ptr, sublen));
|
||||
tok = nullptr) {
|
||||
if (((tok + sublen) >= ep) && (retval != 0) && full) {
|
||||
if (sublen > 0) memmove(buffer, tok, sublen);
|
||||
len = sublen;
|
||||
break;
|
||||
}
|
||||
if ((sublen > 0) && *tok) {
|
||||
log(tok, sublen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LogKlog::calculateCorrection(const log_time& monotonic,
|
||||
const char* real_string, ssize_t len) {
|
||||
static const char real_format[] = "%Y-%m-%d %H:%M:%S.%09q UTC";
|
||||
if (len < (ssize_t)(strlen(real_format) + 5)) return;
|
||||
|
||||
log_time real(log_time::EPOCH);
|
||||
const char* ep = real.strptime(real_string, real_format);
|
||||
if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
|
||||
return;
|
||||
}
|
||||
// kernel report UTC, log_time::strptime is localtime from calendar.
|
||||
// Bionic and liblog strptime does not support %z or %Z to pick up
|
||||
// timezone so we are calculating our own correction.
|
||||
time_t now = real.tv_sec;
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
tm.tm_isdst = -1;
|
||||
localtime_r(&now, &tm);
|
||||
if ((tm.tm_gmtoff < 0) && ((-tm.tm_gmtoff) > (long)real.tv_sec)) {
|
||||
real = log_time(log_time::EPOCH);
|
||||
} else {
|
||||
real.tv_sec += tm.tm_gmtoff;
|
||||
}
|
||||
if (monotonic > real) {
|
||||
correction = log_time(log_time::EPOCH);
|
||||
} else {
|
||||
correction = real - monotonic;
|
||||
}
|
||||
}
|
||||
|
||||
log_time LogKlog::sniffTime(const char*& buf, ssize_t len, bool reverse) {
|
||||
log_time now(log_time::EPOCH);
|
||||
if (len <= 0) return now;
|
||||
|
||||
const char* cp = nullptr;
|
||||
if ((len > 10) && (*buf == '[')) {
|
||||
cp = now.strptime(buf, "[ %s.%q]"); // can index beyond buffer bounds
|
||||
if (cp && (cp > &buf[len - 1])) cp = nullptr;
|
||||
}
|
||||
if (cp) {
|
||||
len -= cp - buf;
|
||||
if ((len > 0) && isspace(*cp)) {
|
||||
++cp;
|
||||
--len;
|
||||
}
|
||||
buf = cp;
|
||||
|
||||
const char* b;
|
||||
if (((b = android::strnstr(cp, len, suspendStr))) &&
|
||||
(((b += strlen(suspendStr)) - cp) < len)) {
|
||||
len -= b - cp;
|
||||
calculateCorrection(now, b, len);
|
||||
} else if (((b = android::strnstr(cp, len, resumeStr))) &&
|
||||
(((b += strlen(resumeStr)) - cp) < len)) {
|
||||
len -= b - cp;
|
||||
calculateCorrection(now, b, len);
|
||||
} else if (((b = android::strnstr(cp, len, healthdStr))) &&
|
||||
(((b += strlen(healthdStr)) - cp) < len) &&
|
||||
((b = android::strnstr(b, len -= b - cp, batteryStr))) &&
|
||||
(((b += strlen(batteryStr)) - cp) < len)) {
|
||||
// NB: healthd is roughly 150us late, so we use it instead to
|
||||
// trigger a check for ntp-induced or hardware clock drift.
|
||||
log_time real(CLOCK_REALTIME);
|
||||
log_time mono(CLOCK_MONOTONIC);
|
||||
correction = (real < mono) ? log_time(log_time::EPOCH) : (real - mono);
|
||||
} else if (((b = android::strnstr(cp, len, suspendedStr))) &&
|
||||
(((b += strlen(suspendStr)) - cp) < len)) {
|
||||
len -= b - cp;
|
||||
log_time real(log_time::EPOCH);
|
||||
char* endp;
|
||||
real.tv_sec = strtol(b, &endp, 10);
|
||||
if ((*endp == '.') && ((endp - b) < len)) {
|
||||
unsigned long multiplier = NS_PER_SEC;
|
||||
real.tv_nsec = 0;
|
||||
len -= endp - b;
|
||||
while (--len && isdigit(*++endp) && (multiplier /= 10)) {
|
||||
real.tv_nsec += (*endp - '0') * multiplier;
|
||||
}
|
||||
if (reverse) {
|
||||
if (real > correction) {
|
||||
correction = log_time(log_time::EPOCH);
|
||||
} else {
|
||||
correction -= real;
|
||||
}
|
||||
} else {
|
||||
correction += real;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convertMonotonicToReal(now);
|
||||
} else {
|
||||
now = log_time(CLOCK_REALTIME);
|
||||
}
|
||||
return now;
|
||||
}
|
||||
|
||||
pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
|
||||
if (len <= 0) return 0;
|
||||
|
||||
const char* cp = buf;
|
||||
// sscanf does a strlen, let's check if the string is not nul terminated.
|
||||
// pseudo out-of-bounds access since we always have an extra char on buffer.
|
||||
if (((ssize_t)strnlen(cp, len) == len) && cp[len]) {
|
||||
return 0;
|
||||
}
|
||||
// HTC kernels with modified printk "c0 1648 "
|
||||
if ((len > 9) && (cp[0] == 'c') && isdigit(cp[1]) &&
|
||||
(isdigit(cp[2]) || (cp[2] == ' ')) && (cp[3] == ' ')) {
|
||||
bool gotDigit = false;
|
||||
int i;
|
||||
for (i = 4; i < 9; ++i) {
|
||||
if (isdigit(cp[i])) {
|
||||
gotDigit = true;
|
||||
} else if (gotDigit || (cp[i] != ' ')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((i == 9) && (cp[i] == ' ')) {
|
||||
int pid = 0;
|
||||
char placeholder;
|
||||
if (sscanf(cp + 4, "%d%c", &pid, &placeholder) == 2) {
|
||||
buf = cp + 10; // skip-it-all
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (len) {
|
||||
// Mediatek kernels with modified printk
|
||||
if (*cp == '[') {
|
||||
int pid = 0;
|
||||
char placeholder;
|
||||
if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &placeholder) == 2) {
|
||||
return pid;
|
||||
}
|
||||
break; // Only the first one
|
||||
}
|
||||
++cp;
|
||||
--len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// kernel log prefix, convert to a kernel log priority number
|
||||
static int parseKernelPrio(const char*& buf, ssize_t len) {
|
||||
int pri = LOG_USER | LOG_INFO;
|
||||
const char* cp = buf;
|
||||
if ((len > 0) && (*cp == '<')) {
|
||||
pri = 0;
|
||||
while (--len && isdigit(*++cp)) {
|
||||
pri = (pri * 10) + *cp - '0';
|
||||
}
|
||||
if ((len > 0) && (*cp == '>')) {
|
||||
++cp;
|
||||
} else {
|
||||
cp = buf;
|
||||
pri = LOG_USER | LOG_INFO;
|
||||
}
|
||||
buf = cp;
|
||||
}
|
||||
return pri;
|
||||
}
|
||||
|
||||
// Convert kernel log priority number into an Android Logger priority number
|
||||
static int convertKernelPrioToAndroidPrio(int pri) {
|
||||
switch (pri & LOG_PRIMASK) {
|
||||
case LOG_EMERG:
|
||||
case LOG_ALERT:
|
||||
case LOG_CRIT:
|
||||
return ANDROID_LOG_FATAL;
|
||||
|
||||
case LOG_ERR:
|
||||
return ANDROID_LOG_ERROR;
|
||||
|
||||
case LOG_WARNING:
|
||||
return ANDROID_LOG_WARN;
|
||||
|
||||
default:
|
||||
case LOG_NOTICE:
|
||||
case LOG_INFO:
|
||||
break;
|
||||
|
||||
case LOG_DEBUG:
|
||||
return ANDROID_LOG_DEBUG;
|
||||
}
|
||||
|
||||
return ANDROID_LOG_INFO;
|
||||
}
|
||||
|
||||
static const char* strnrchr(const char* s, ssize_t len, char c) {
|
||||
const char* save = nullptr;
|
||||
for (; len > 0; ++s, len--) {
|
||||
if (*s == c) {
|
||||
save = s;
|
||||
}
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
//
|
||||
// log a message into the kernel log buffer
|
||||
//
|
||||
// Filter rules to parse <PRI> <TIME> <tag> and <message> in order for
|
||||
// them to appear correct in the logcat output:
|
||||
//
|
||||
// LOG_KERN (0):
|
||||
// <PRI>[<TIME>] <tag> ":" <message>
|
||||
// <PRI>[<TIME>] <tag> <tag> ":" <message>
|
||||
// <PRI>[<TIME>] <tag> <tag>_work ":" <message>
|
||||
// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message>
|
||||
// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message>
|
||||
// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message>
|
||||
// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message>
|
||||
// <PRI>[<TIME>] "[INFO]"<tag> : <message>
|
||||
// <PRI>[<TIME>] "------------[ cut here ]------------" (?)
|
||||
// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?)
|
||||
// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS
|
||||
// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP:
|
||||
// <PRI+TAG>[<TIME>] (see sys/syslog.h)
|
||||
// Observe:
|
||||
// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM:
|
||||
// Maximum tag words = 2
|
||||
// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get.
|
||||
// Not a Tag if there is no message content.
|
||||
// leading additional spaces means no tag, inherit last tag.
|
||||
// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:"
|
||||
// Drop:
|
||||
// empty messages
|
||||
// messages with ' audit(' in them if auditd is running
|
||||
// logd.klogd:
|
||||
// return -1 if message logd.klogd: <signature>
|
||||
//
|
||||
int LogKlog::log(const char* buf, ssize_t len) {
|
||||
if (auditd && android::strnstr(buf, len, auditStr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* p = buf;
|
||||
int pri = parseKernelPrio(p, len);
|
||||
|
||||
log_time now = sniffTime(p, len - (p - buf), false);
|
||||
|
||||
// sniff for start marker
|
||||
const char* start = android::strnstr(p, len - (p - buf), klogdStr);
|
||||
if (start) {
|
||||
uint64_t sig = strtoll(start + strlen(klogdStr), nullptr, 10);
|
||||
if (sig == signature.nsec()) {
|
||||
if (initialized) {
|
||||
enableLogging = true;
|
||||
} else {
|
||||
enableLogging = false;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!enableLogging) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse pid, tid and uid
|
||||
const pid_t pid = sniffPid(p, len - (p - buf));
|
||||
const pid_t tid = pid;
|
||||
uid_t uid = AID_ROOT;
|
||||
if (pid) {
|
||||
uid = stats_->PidToUid(pid);
|
||||
}
|
||||
|
||||
// Parse (rules at top) to pull out a tag from the incoming kernel message.
|
||||
// Some may view the following as an ugly heuristic, the desire is to
|
||||
// beautify the kernel logs into an Android Logging format; the goal is
|
||||
// admirable but costly.
|
||||
while ((p < &buf[len]) && (isspace(*p) || !*p)) {
|
||||
++p;
|
||||
}
|
||||
if (p >= &buf[len]) { // timestamp, no content
|
||||
return 0;
|
||||
}
|
||||
start = p;
|
||||
const char* tag = "";
|
||||
const char* etag = tag;
|
||||
ssize_t taglen = len - (p - buf);
|
||||
const char* bt = p;
|
||||
|
||||
static const char infoBrace[] = "[INFO]";
|
||||
static const ssize_t infoBraceLen = strlen(infoBrace);
|
||||
if ((taglen >= infoBraceLen) &&
|
||||
!fastcmp<strncmp>(p, infoBrace, infoBraceLen)) {
|
||||
// <PRI>[<TIME>] "[INFO]"<tag> ":" message
|
||||
bt = p + infoBraceLen;
|
||||
taglen -= infoBraceLen;
|
||||
}
|
||||
|
||||
const char* et;
|
||||
for (et = bt; (taglen > 0) && *et && (*et != ':') && !isspace(*et);
|
||||
++et, --taglen) {
|
||||
// skip ':' within [ ... ]
|
||||
if (*et == '[') {
|
||||
while ((taglen > 0) && *et && *et != ']') {
|
||||
++et;
|
||||
--taglen;
|
||||
}
|
||||
if (taglen <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const char* cp;
|
||||
for (cp = et; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
|
||||
}
|
||||
|
||||
// Validate tag
|
||||
ssize_t size = et - bt;
|
||||
if ((taglen > 0) && (size > 0)) {
|
||||
if (*cp == ':') {
|
||||
// ToDo: handle case insensitive colon separated logging stutter:
|
||||
// <tag> : <tag>: ...
|
||||
|
||||
// One Word
|
||||
tag = bt;
|
||||
etag = et;
|
||||
p = cp + 1;
|
||||
} else if ((taglen > size) && (tolower(*bt) == tolower(*cp))) {
|
||||
// clean up any tag stutter
|
||||
if (!fastcmp<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
|
||||
// <PRI>[<TIME>] <tag> <tag> : message
|
||||
// <PRI>[<TIME>] <tag> <tag>: message
|
||||
// <PRI>[<TIME>] <tag> '<tag>.<num>' : message
|
||||
// <PRI>[<TIME>] <tag> '<tag><num>' : message
|
||||
// <PRI>[<TIME>] <tag> '<tag><stuff>' : message
|
||||
const char* b = cp;
|
||||
cp += size;
|
||||
taglen -= size;
|
||||
while ((--taglen > 0) && !isspace(*++cp) && (*cp != ':')) {
|
||||
}
|
||||
const char* e;
|
||||
for (e = cp; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
|
||||
}
|
||||
if ((taglen > 0) && (*cp == ':')) {
|
||||
tag = b;
|
||||
etag = e;
|
||||
p = cp + 1;
|
||||
}
|
||||
} else {
|
||||
// what about <PRI>[<TIME>] <tag>_host '<tag><stuff>' : message
|
||||
static const char host[] = "_host";
|
||||
static const ssize_t hostlen = strlen(host);
|
||||
if ((size > hostlen) &&
|
||||
!fastcmp<strncmp>(bt + size - hostlen, host, hostlen) &&
|
||||
!fastcmp<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
|
||||
const char* b = cp;
|
||||
cp += size - hostlen;
|
||||
taglen -= size - hostlen;
|
||||
if (*cp == '.') {
|
||||
while ((--taglen > 0) && !isspace(*++cp) &&
|
||||
(*cp != ':')) {
|
||||
}
|
||||
const char* e;
|
||||
for (e = cp; (taglen > 0) && isspace(*cp);
|
||||
++cp, --taglen) {
|
||||
}
|
||||
if ((taglen > 0) && (*cp == ':')) {
|
||||
tag = b;
|
||||
etag = e;
|
||||
p = cp + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
goto twoWord;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// <PRI>[<TIME>] <tag> <stuff>' : message
|
||||
twoWord:
|
||||
while ((--taglen > 0) && !isspace(*++cp) && (*cp != ':')) {
|
||||
}
|
||||
const char* e;
|
||||
for (e = cp; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
|
||||
}
|
||||
// Two words
|
||||
if ((taglen > 0) && (*cp == ':')) {
|
||||
tag = bt;
|
||||
etag = e;
|
||||
p = cp + 1;
|
||||
}
|
||||
}
|
||||
} // else no tag
|
||||
|
||||
static const char cpu[] = "CPU";
|
||||
static const ssize_t cpuLen = strlen(cpu);
|
||||
static const char warning[] = "WARNING";
|
||||
static const ssize_t warningLen = strlen(warning);
|
||||
static const char error[] = "ERROR";
|
||||
static const ssize_t errorLen = strlen(error);
|
||||
static const char info[] = "INFO";
|
||||
static const ssize_t infoLen = strlen(info);
|
||||
|
||||
size = etag - tag;
|
||||
if ((size <= 1) ||
|
||||
// register names like x9
|
||||
((size == 2) && (isdigit(tag[0]) || isdigit(tag[1]))) ||
|
||||
// register names like x18 but not driver names like en0
|
||||
((size == 3) && (isdigit(tag[1]) && isdigit(tag[2]))) ||
|
||||
// ignore
|
||||
((size == cpuLen) && !fastcmp<strncmp>(tag, cpu, cpuLen)) ||
|
||||
((size == warningLen) && !fastcmp<strncasecmp>(tag, warning, warningLen)) ||
|
||||
((size == errorLen) && !fastcmp<strncasecmp>(tag, error, errorLen)) ||
|
||||
((size == infoLen) && !fastcmp<strncasecmp>(tag, info, infoLen))) {
|
||||
p = start;
|
||||
etag = tag = "";
|
||||
}
|
||||
|
||||
// Suppress additional stutter in tag:
|
||||
// eg: [143:healthd]healthd -> [143:healthd]
|
||||
taglen = etag - tag;
|
||||
// Mediatek-special printk induced stutter
|
||||
const char* mp = strnrchr(tag, taglen, ']');
|
||||
if (mp && (++mp < etag)) {
|
||||
ssize_t s = etag - mp;
|
||||
if (((s + s) < taglen) && !fastcmp<memcmp>(mp, mp - 1 - s, s)) {
|
||||
taglen = mp - tag;
|
||||
}
|
||||
}
|
||||
// Deal with sloppy and simplistic harmless p = cp + 1 etc above.
|
||||
if (len < (p - buf)) {
|
||||
p = &buf[len];
|
||||
}
|
||||
// skip leading space
|
||||
while ((p < &buf[len]) && (isspace(*p) || !*p)) {
|
||||
++p;
|
||||
}
|
||||
// truncate trailing space or nuls
|
||||
ssize_t b = len - (p - buf);
|
||||
while ((b > 0) && (isspace(p[b - 1]) || !p[b - 1])) {
|
||||
--b;
|
||||
}
|
||||
// trick ... allow tag with empty content to be logged. log() drops empty
|
||||
if ((b <= 0) && (taglen > 0)) {
|
||||
p = " ";
|
||||
b = 1;
|
||||
}
|
||||
// This shouldn't happen, but clamp the size if it does.
|
||||
if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
|
||||
b = LOGGER_ENTRY_MAX_PAYLOAD;
|
||||
}
|
||||
if (taglen > LOGGER_ENTRY_MAX_PAYLOAD) {
|
||||
taglen = LOGGER_ENTRY_MAX_PAYLOAD;
|
||||
}
|
||||
// calculate buffer copy requirements
|
||||
ssize_t n = 1 + taglen + 1 + b + 1;
|
||||
// Extra checks for likely impossible cases.
|
||||
if ((taglen > n) || (b > n) || (n > (ssize_t)USHRT_MAX) || (n <= 0)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// Careful.
|
||||
// We are using the stack to house the log buffer for speed reasons.
|
||||
// If we malloc'd this buffer, we could get away without n's USHRT_MAX
|
||||
// test above, but we would then required a max(n, USHRT_MAX) as
|
||||
// truncating length argument to logbuf->log() below. Gain is protection
|
||||
// against stack corruption and speedup, loss is truncated long-line content.
|
||||
char newstr[n];
|
||||
char* np = newstr;
|
||||
|
||||
// Convert priority into single-byte Android logger priority
|
||||
*np = convertKernelPrioToAndroidPrio(pri);
|
||||
++np;
|
||||
|
||||
// Copy parsed tag following priority
|
||||
memcpy(np, tag, taglen);
|
||||
np += taglen;
|
||||
*np = '\0';
|
||||
++np;
|
||||
|
||||
// Copy main message to the remainder
|
||||
memcpy(np, p, b);
|
||||
np[b] = '\0';
|
||||
|
||||
{
|
||||
// Watch out for singular race conditions with timezone causing near
|
||||
// integer quarter-hour jumps in the time and compensate accordingly.
|
||||
// Entries will be temporal within near_seconds * 2. b/21868540
|
||||
static uint32_t vote_time[3];
|
||||
vote_time[2] = vote_time[1];
|
||||
vote_time[1] = vote_time[0];
|
||||
vote_time[0] = now.tv_sec;
|
||||
|
||||
if (vote_time[1] && vote_time[2]) {
|
||||
static const unsigned near_seconds = 10;
|
||||
static const unsigned timezones_seconds = 900;
|
||||
int diff0 = (vote_time[0] - vote_time[1]) / near_seconds;
|
||||
unsigned abs0 = (diff0 < 0) ? -diff0 : diff0;
|
||||
int diff1 = (vote_time[1] - vote_time[2]) / near_seconds;
|
||||
unsigned abs1 = (diff1 < 0) ? -diff1 : diff1;
|
||||
if ((abs1 <= 1) && // last two were in agreement on timezone
|
||||
((abs0 + 1) % (timezones_seconds / near_seconds)) <= 2) {
|
||||
abs0 = (abs0 + 1) / (timezones_seconds / near_seconds) *
|
||||
timezones_seconds;
|
||||
now.tv_sec -= (diff0 < 0) ? -abs0 : abs0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log message
|
||||
int rc = logbuf->Log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
|
||||
|
||||
return rc;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue