mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2026-04-19 09:07:06 -04:00
Changes
- Remove crash dumper (unused yet..) - Fix random crashes on custom controls
This commit is contained in:
@@ -128,6 +128,7 @@ public class PojavLoginActivity extends BaseActivity
|
||||
if (revokeCount >= 3) {
|
||||
Toast.makeText(PojavLoginActivity.this, R.string.toast_permission_denied, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return 0;
|
||||
}
|
||||
|
||||
requestStoragePermission();
|
||||
|
||||
@@ -156,7 +156,12 @@ public class ControlButton extends Button implements OnLongClickListener, OnTouc
|
||||
if (getParent() != null) {
|
||||
((ControlLayout) getParent()).hideAllHandleViews();
|
||||
}
|
||||
mHandleView.show();
|
||||
|
||||
try {
|
||||
mHandleView.show();
|
||||
} catch (Throwable th) {
|
||||
th.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public class ControlLayout extends FrameLayout
|
||||
|
||||
public void loadLayout(CustomControls controlLayout) {
|
||||
if (mModifiable) {
|
||||
hideAllHandleViews();
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsEnvironment_getNumScreens(JNIEnv
|
||||
* 0 otherwise
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsEnvironment_checkShmExt(JNIEnv *env, jclass cls, jboolean verbose) {
|
||||
// This could be return 0 once MITShm implementation added
|
||||
return (jint) -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
BasedOnStyle: Google
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
|
||||
ColumnLimit: 100
|
||||
CommentPragmas: NOLINT:.*
|
||||
DerivePointerAlignment: false
|
||||
IndentWidth: 2
|
||||
ContinuationIndentWidth: 2
|
||||
PointerAlignment: Left
|
||||
TabWidth: 2
|
||||
UseTab: Never
|
||||
PenaltyExcessCharacter: 32
|
||||
|
||||
Cpp11BracedListStyle: false
|
||||
@@ -1,79 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
common_cppflags := \
|
||||
-std=gnu++11 \
|
||||
-W \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wunused \
|
||||
-Werror \
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
backtrace.cpp \
|
||||
debuggerd.cpp \
|
||||
elf_utils.cpp \
|
||||
getevent.cpp \
|
||||
signal_sender.cpp \
|
||||
tombstone.cpp \
|
||||
utility.cpp \
|
||||
selinux_fake.cpp
|
||||
|
||||
LOCAL_SRC_FILES_arm := arm/machine.cpp
|
||||
LOCAL_SRC_FILES_arm64 := arm64/machine.cpp
|
||||
LOCAL_SRC_FILES_x86 := x86/machine.cpp
|
||||
LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
|
||||
|
||||
LOCAL_CPPFLAGS := $(common_cppflags) \
|
||||
-I$(HERE_PATH)/crash_dump/libbase/include
|
||||
|
||||
LOCAL_INIT_RC_32 := debuggerd.rc
|
||||
LOCAL_INIT_RC_64 := debuggerd64.rc
|
||||
|
||||
ifeq ($(TARGET_IS_64_BIT),true)
|
||||
LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
|
||||
endif
|
||||
|
||||
LOCAL_LDLIBS := -llog
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libbacktrace \
|
||||
libcrashdumpbase
|
||||
|
||||
LOCAL_CLANG := true
|
||||
|
||||
LOCAL_MODULE := crashdump
|
||||
LOCAL_MULTILIB := both
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := crasher.c
|
||||
LOCAL_SRC_FILES_arm := arm/crashglue.S
|
||||
LOCAL_SRC_FILES_arm64 := arm64/crashglue.S
|
||||
LOCAL_SRC_FILES_mips := mips/crashglue.S
|
||||
LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
|
||||
LOCAL_SRC_FILES_x86 := x86/crashglue.S
|
||||
LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_CFLAGS += -fstack-protector-all -Werror -Wno-free-nonheap-object -Wno-date-time \
|
||||
-I$(HERE_PATH)/crash_dump/libbase/include
|
||||
|
||||
#LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_SHARED_LIBRARIES := libcutils liblog libc
|
||||
|
||||
# The arm emulator has VFP but not VFPv3-D32.
|
||||
ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
|
||||
LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
|
||||
endif
|
||||
|
||||
LOCAL_MODULE := crasher
|
||||
LOCAL_MODULE_STEM_32 := crasher
|
||||
LOCAL_MODULE_STEM_64 := crasher64
|
||||
LOCAL_MULTILIB := both
|
||||
|
||||
# include $(BUILD_EXECUTABLE)
|
||||
|
||||
@@ -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,64 +0,0 @@
|
||||
.globl crash1
|
||||
.type crash1, %function
|
||||
crash1:
|
||||
ldr r0, =0xa5a50000
|
||||
ldr r1, =0xa5a50001
|
||||
ldr r2, =0xa5a50002
|
||||
ldr r3, =0xa5a50003
|
||||
ldr r4, =0xa5a50004
|
||||
ldr r5, =0xa5a50005
|
||||
ldr r6, =0xa5a50006
|
||||
ldr r7, =0xa5a50007
|
||||
ldr r8, =0xa5a50008
|
||||
ldr r9, =0xa5a50009
|
||||
ldr r10, =0xa5a50010
|
||||
ldr r11, =0xa5a50011
|
||||
ldr r12, =0xa5a50012
|
||||
|
||||
|
||||
fconstd d0, #0
|
||||
fconstd d1, #1
|
||||
fconstd d2, #2
|
||||
fconstd d3, #3
|
||||
fconstd d4, #4
|
||||
fconstd d5, #5
|
||||
fconstd d6, #6
|
||||
fconstd d7, #7
|
||||
fconstd d8, #8
|
||||
fconstd d9, #9
|
||||
fconstd d10, #10
|
||||
fconstd d11, #11
|
||||
fconstd d12, #12
|
||||
fconstd d13, #13
|
||||
fconstd d14, #14
|
||||
fconstd d15, #15
|
||||
#if defined(HAS_VFP_D32)
|
||||
fconstd d16, #16
|
||||
fconstd d17, #17
|
||||
fconstd d18, #18
|
||||
fconstd d19, #19
|
||||
fconstd d20, #20
|
||||
fconstd d21, #21
|
||||
fconstd d22, #22
|
||||
fconstd d23, #23
|
||||
fconstd d24, #24
|
||||
fconstd d25, #25
|
||||
fconstd d26, #26
|
||||
fconstd d27, #27
|
||||
fconstd d28, #28
|
||||
fconstd d29, #29
|
||||
fconstd d30, #30
|
||||
fconstd d31, #31
|
||||
#endif
|
||||
|
||||
mov lr, #0
|
||||
ldr lr, [lr]
|
||||
b .
|
||||
|
||||
.globl crashnostack
|
||||
.type crashnostack, %function
|
||||
crashnostack:
|
||||
mov sp, #0
|
||||
mov r0, #0
|
||||
ldr r0, [r0]
|
||||
b .
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "DEBUG"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include "machine.h"
|
||||
#include "utility.h"
|
||||
|
||||
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
|
||||
pt_regs regs;
|
||||
if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, ®s)) {
|
||||
ALOGE("cannot get registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
static const char reg_names[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp";
|
||||
|
||||
for (int reg = 0; reg < 14; reg++) {
|
||||
dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", ®_names[reg * 2]);
|
||||
}
|
||||
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_pc), "code around pc:");
|
||||
|
||||
if (regs.ARM_pc != regs.ARM_lr) {
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_lr), "code around lr:");
|
||||
}
|
||||
}
|
||||
|
||||
void dump_registers(log_t* log, pid_t tid) {
|
||||
pt_regs r;
|
||||
if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
|
||||
ALOGE("cannot get registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG(log, logtype::REGISTERS, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
|
||||
static_cast<uint32_t>(r.ARM_r0), static_cast<uint32_t>(r.ARM_r1),
|
||||
static_cast<uint32_t>(r.ARM_r2), static_cast<uint32_t>(r.ARM_r3));
|
||||
_LOG(log, logtype::REGISTERS, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
|
||||
static_cast<uint32_t>(r.ARM_r4), static_cast<uint32_t>(r.ARM_r5),
|
||||
static_cast<uint32_t>(r.ARM_r6), static_cast<uint32_t>(r.ARM_r7));
|
||||
_LOG(log, logtype::REGISTERS, " r8 %08x r9 %08x sl %08x fp %08x\n",
|
||||
static_cast<uint32_t>(r.ARM_r8), static_cast<uint32_t>(r.ARM_r9),
|
||||
static_cast<uint32_t>(r.ARM_r10), static_cast<uint32_t>(r.ARM_fp));
|
||||
_LOG(log, logtype::REGISTERS, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
|
||||
static_cast<uint32_t>(r.ARM_ip), static_cast<uint32_t>(r.ARM_sp),
|
||||
static_cast<uint32_t>(r.ARM_lr), static_cast<uint32_t>(r.ARM_pc),
|
||||
static_cast<uint32_t>(r.ARM_cpsr));
|
||||
|
||||
user_vfp vfp_regs;
|
||||
if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
|
||||
ALOGE("cannot get FP registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 32; i += 2) {
|
||||
_LOG(log, logtype::FP_REGISTERS, " d%-2d %016llx d%-2d %016llx\n",
|
||||
i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
|
||||
}
|
||||
_LOG(log, logtype::FP_REGISTERS, " scr %08lx\n", vfp_regs.fpscr);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
.globl crash1
|
||||
.type crash1, %function
|
||||
crash1:
|
||||
ldr x0, =0xa5a50000
|
||||
ldr x1, =0xa5a50001
|
||||
ldr x2, =0xa5a50002
|
||||
ldr x3, =0xa5a50003
|
||||
ldr x4, =0xa5a50004
|
||||
ldr x5, =0xa5a50005
|
||||
ldr x6, =0xa5a50006
|
||||
ldr x7, =0xa5a50007
|
||||
ldr x8, =0xa5a50008
|
||||
ldr x9, =0xa5a50009
|
||||
ldr x10, =0xa5a50010
|
||||
ldr x11, =0xa5a50011
|
||||
ldr x12, =0xa5a50012
|
||||
ldr x13, =0xa5a50013
|
||||
ldr x14, =0xa5a50014
|
||||
ldr x15, =0xa5a50015
|
||||
ldr x16, =0xa5a50016
|
||||
ldr x17, =0xa5a50017
|
||||
ldr x18, =0xa5a50018
|
||||
ldr x19, =0xa5a50019
|
||||
ldr x20, =0xa5a50020
|
||||
ldr x21, =0xa5a50021
|
||||
ldr x22, =0xa5a50022
|
||||
ldr x23, =0xa5a50023
|
||||
ldr x24, =0xa5a50024
|
||||
ldr x25, =0xa5a50025
|
||||
ldr x26, =0xa5a50026
|
||||
ldr x27, =0xa5a50027
|
||||
ldr x28, =0xa5a50028
|
||||
ldr x29, =0xa5a50029
|
||||
|
||||
fmov d0, -1.0 // -1 is more convincing than 0.
|
||||
fmov d1, 1.0
|
||||
fmov d2, 2.0
|
||||
fmov d3, 3.0
|
||||
fmov d4, 4.0
|
||||
fmov d5, 5.0
|
||||
fmov d6, 6.0
|
||||
fmov d7, 7.0
|
||||
fmov d8, 8.0
|
||||
fmov d9, 9.0
|
||||
fmov d10, 10.0
|
||||
fmov d11, 11.0
|
||||
fmov d12, 12.0
|
||||
fmov d13, 13.0
|
||||
fmov d14, 14.0
|
||||
fmov d15, 15.0
|
||||
fmov d16, 16.0
|
||||
fmov d17, 17.0
|
||||
fmov d18, 18.0
|
||||
fmov d19, 19.0
|
||||
fmov d20, 20.0
|
||||
fmov d21, 21.0
|
||||
fmov d22, 22.0
|
||||
fmov d23, 23.0
|
||||
fmov d24, 24.0
|
||||
fmov d25, 25.0
|
||||
fmov d26, 26.0
|
||||
fmov d27, 27.0
|
||||
fmov d28, 28.0
|
||||
fmov d29, 29.0
|
||||
fmov d30, 30.0
|
||||
fmov d31, 31.0
|
||||
|
||||
mov x30, xzr
|
||||
ldr x30, [x30]
|
||||
b .
|
||||
|
||||
|
||||
.globl crashnostack
|
||||
.type crashnostack, %function
|
||||
crashnostack:
|
||||
mov x0, xzr
|
||||
add sp, x0, xzr
|
||||
ldr x0, [x0]
|
||||
b .
|
||||
@@ -1,101 +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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "DEBUG"
|
||||
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include "machine.h"
|
||||
#include "utility.h"
|
||||
|
||||
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
|
||||
struct user_pt_regs regs;
|
||||
struct iovec io;
|
||||
io.iov_base = ®s;
|
||||
io.iov_len = sizeof(regs);
|
||||
|
||||
if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
|
||||
ALOGE("ptrace failed to get registers: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int reg = 0; reg < 31; reg++) {
|
||||
dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg);
|
||||
}
|
||||
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:");
|
||||
|
||||
if (regs.pc != regs.sp) {
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(regs.sp), "code around sp:");
|
||||
}
|
||||
}
|
||||
|
||||
void dump_registers(log_t* log, pid_t tid) {
|
||||
struct user_pt_regs r;
|
||||
struct iovec io;
|
||||
io.iov_base = &r;
|
||||
io.iov_len = sizeof(r);
|
||||
|
||||
if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
|
||||
ALOGE("ptrace error: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 28; i += 4) {
|
||||
_LOG(log, logtype::REGISTERS,
|
||||
" x%-2d %016llx x%-2d %016llx x%-2d %016llx x%-2d %016llx\n",
|
||||
i, r.regs[i],
|
||||
i+1, r.regs[i+1],
|
||||
i+2, r.regs[i+2],
|
||||
i+3, r.regs[i+3]);
|
||||
}
|
||||
|
||||
_LOG(log, logtype::REGISTERS, " x28 %016llx x29 %016llx x30 %016llx\n",
|
||||
r.regs[28], r.regs[29], r.regs[30]);
|
||||
|
||||
_LOG(log, logtype::REGISTERS, " sp %016llx pc %016llx pstate %016llx\n",
|
||||
r.sp, r.pc, r.pstate);
|
||||
|
||||
struct user_fpsimd_state f;
|
||||
io.iov_base = &f;
|
||||
io.iov_len = sizeof(f);
|
||||
|
||||
if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
|
||||
ALOGE("ptrace error: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i += 2) {
|
||||
_LOG(log, logtype::FP_REGISTERS,
|
||||
" v%-2d %016" PRIx64 "%016" PRIx64 " v%-2d %016" PRIx64 "%016" PRIx64 "\n",
|
||||
i,
|
||||
static_cast<uint64_t>(f.vregs[i] >> 64),
|
||||
static_cast<uint64_t>(f.vregs[i]),
|
||||
i+1,
|
||||
static_cast<uint64_t>(f.vregs[i+1] >> 64),
|
||||
static_cast<uint64_t>(f.vregs[i+1]));
|
||||
}
|
||||
_LOG(log, logtype::FP_REGISTERS, " fpsr %08x fpcr %08x\n", f.fpsr, f.fpcr);
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 "DEBUG"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
static void dump_process_header(log_t* log, pid_t pid) {
|
||||
char path[PATH_MAX];
|
||||
char procnamebuf[1024];
|
||||
char* procname = NULL;
|
||||
FILE* fp;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||
if ((fp = fopen(path, "r"))) {
|
||||
procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm tm;
|
||||
localtime_r(&t, &tm);
|
||||
char timestr[64];
|
||||
strftime(timestr, sizeof(timestr), "%F %T", &tm);
|
||||
_LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
|
||||
|
||||
if (procname) {
|
||||
_LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", procname);
|
||||
}
|
||||
_LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
|
||||
}
|
||||
|
||||
static void dump_process_footer(log_t* log, pid_t pid) {
|
||||
_LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
|
||||
}
|
||||
|
||||
static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid) {
|
||||
char path[PATH_MAX];
|
||||
char threadnamebuf[1024];
|
||||
char* threadname = NULL;
|
||||
FILE* fp;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/comm", tid);
|
||||
if ((fp = fopen(path, "r"))) {
|
||||
threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
|
||||
fclose(fp);
|
||||
if (threadname) {
|
||||
size_t len = strlen(threadname);
|
||||
if (len && threadname[len - 1] == '\n') {
|
||||
threadname[len - 1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);
|
||||
|
||||
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
|
||||
if (backtrace->Unwind(0)) {
|
||||
dump_backtrace_to_log(backtrace.get(), log, " ");
|
||||
} else {
|
||||
ALOGE("Unwind failed: tid = %d: %s", tid,
|
||||
backtrace->GetErrorString(backtrace->GetError()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid,
|
||||
const std::set<pid_t>& siblings, std::string* amfd_data) {
|
||||
log_t log;
|
||||
log.tfd = fd;
|
||||
log.amfd_data = amfd_data;
|
||||
|
||||
dump_process_header(&log, pid);
|
||||
dump_thread(&log, map, pid, tid);
|
||||
|
||||
for (pid_t sibling : siblings) {
|
||||
dump_thread(&log, map, pid, sibling);
|
||||
}
|
||||
|
||||
dump_process_footer(&log, pid);
|
||||
}
|
||||
|
||||
void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) {
|
||||
for (size_t i = 0; i < backtrace->NumFrames(); i++) {
|
||||
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str());
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 _DEBUGGERD_BACKTRACE_H
|
||||
#define _DEBUGGERD_BACKTRACE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
class Backtrace;
|
||||
class BacktraceMap;
|
||||
|
||||
// Dumps a backtrace using a format similar to what Dalvik uses so that the result
|
||||
// can be intermixed in a bug report.
|
||||
void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid,
|
||||
const std::set<pid_t>& siblings, std::string* amfd_data);
|
||||
|
||||
/* Dumps the backtrace in the backtrace data structure to the log. */
|
||||
void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
|
||||
|
||||
#endif // _DEBUGGERD_BACKTRACE_H
|
||||
@@ -1,215 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/sockets.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#ifndef __unused
|
||||
#define __unused __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
extern const char* __progname;
|
||||
|
||||
void crash1(void);
|
||||
void crashnostack(void);
|
||||
static int do_action(const char* arg);
|
||||
|
||||
static void maybe_abort() {
|
||||
if (time(0) != 42) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static char* smash_stack_dummy_buf;
|
||||
__attribute__ ((noinline)) static void smash_stack_dummy_function(volatile int* plen) {
|
||||
smash_stack_dummy_buf[*plen] = 0;
|
||||
}
|
||||
|
||||
// This must be marked with "__attribute__ ((noinline))", to ensure the
|
||||
// compiler generates the proper stack guards around this function.
|
||||
// Assign local array address to global variable to force stack guards.
|
||||
// Use another noinline function to corrupt the stack.
|
||||
__attribute__ ((noinline)) static int smash_stack(volatile int* plen) {
|
||||
printf("crasher: deliberately corrupting stack...\n");
|
||||
|
||||
char buf[128];
|
||||
smash_stack_dummy_buf = buf;
|
||||
// This should corrupt stack guards and make process abort.
|
||||
smash_stack_dummy_function(plen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Winfinite-recursion"
|
||||
#endif
|
||||
|
||||
static void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
|
||||
|
||||
__attribute__((noinline)) static void overflow_stack(void* p) {
|
||||
void* buf[1];
|
||||
buf[0] = p;
|
||||
global = buf;
|
||||
overflow_stack(&buf);
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
static void *noisy(void *x)
|
||||
{
|
||||
char c = (uintptr_t) x;
|
||||
for(;;) {
|
||||
usleep(250*1000);
|
||||
write(2, &c, 1);
|
||||
if(c == 'C') *((volatile unsigned*) 0) = 42;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ctest()
|
||||
{
|
||||
pthread_t thr;
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&thr, &attr, noisy, (void*) 'A');
|
||||
pthread_create(&thr, &attr, noisy, (void*) 'B');
|
||||
pthread_create(&thr, &attr, noisy, (void*) 'C');
|
||||
for(;;) ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void* thread_callback(void* raw_arg)
|
||||
{
|
||||
return (void*) (uintptr_t) do_action((const char*) raw_arg);
|
||||
}
|
||||
|
||||
static int do_action_on_thread(const char* arg)
|
||||
{
|
||||
pthread_t t;
|
||||
pthread_create(&t, NULL, thread_callback, (void*) arg);
|
||||
void* result = NULL;
|
||||
pthread_join(t, &result);
|
||||
return (int) (uintptr_t) result;
|
||||
}
|
||||
|
||||
__attribute__((noinline)) static int crash3(int a) {
|
||||
*((int*) 0xdead) = a;
|
||||
return a*4;
|
||||
}
|
||||
|
||||
__attribute__((noinline)) static int crash2(int a) {
|
||||
a = crash3(a) + 2;
|
||||
return a*3;
|
||||
}
|
||||
|
||||
__attribute__((noinline)) static int crash(int a) {
|
||||
a = crash2(a) + 1;
|
||||
return a*2;
|
||||
}
|
||||
|
||||
static void abuse_heap() {
|
||||
char buf[16];
|
||||
free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
|
||||
}
|
||||
|
||||
static void sigsegv_non_null() {
|
||||
int* a = (int *)(&do_action);
|
||||
*a = 42;
|
||||
}
|
||||
|
||||
static int do_action(const char* arg)
|
||||
{
|
||||
fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
|
||||
|
||||
if (!strncmp(arg, "thread-", strlen("thread-"))) {
|
||||
return do_action_on_thread(arg + strlen("thread-"));
|
||||
} else if (!strcmp(arg, "SIGSEGV-non-null")) {
|
||||
sigsegv_non_null();
|
||||
} else if (!strcmp(arg, "smash-stack")) {
|
||||
volatile int len = 128;
|
||||
return smash_stack(&len);
|
||||
} else if (!strcmp(arg, "stack-overflow")) {
|
||||
overflow_stack(NULL);
|
||||
} else if (!strcmp(arg, "nostack")) {
|
||||
crashnostack();
|
||||
} else if (!strcmp(arg, "ctest")) {
|
||||
return ctest();
|
||||
} else if (!strcmp(arg, "exit")) {
|
||||
exit(1);
|
||||
} else if (!strcmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
|
||||
return crash(42);
|
||||
} else if (!strcmp(arg, "abort")) {
|
||||
maybe_abort();
|
||||
} else if (!strcmp(arg, "assert")) {
|
||||
__assert("some_file.c", 123, "false");
|
||||
} else if (!strcmp(arg, "assert2")) {
|
||||
__assert2("some_file.c", 123, "some_function", "false");
|
||||
} else if (!strcmp(arg, "LOG_ALWAYS_FATAL")) {
|
||||
LOG_ALWAYS_FATAL("hello %s", "world");
|
||||
} else if (!strcmp(arg, "LOG_ALWAYS_FATAL_IF")) {
|
||||
LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
|
||||
} else if (!strcmp(arg, "SIGFPE")) {
|
||||
raise(SIGFPE);
|
||||
return EXIT_SUCCESS;
|
||||
} else if (!strcmp(arg, "SIGTRAP")) {
|
||||
raise(SIGTRAP);
|
||||
return EXIT_SUCCESS;
|
||||
} else if (!strcmp(arg, "heap-usage")) {
|
||||
abuse_heap();
|
||||
} else if (!strcmp(arg, "SIGSEGV-unmapped")) {
|
||||
char* map = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
munmap(map, sizeof(int));
|
||||
map[0] = '8';
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s OP\n", __progname);
|
||||
fprintf(stderr, "where OP is:\n");
|
||||
fprintf(stderr, " smash-stack overwrite a stack-guard canary\n");
|
||||
fprintf(stderr, " stack-overflow recurse until the stack overflows\n");
|
||||
fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n");
|
||||
fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
|
||||
fprintf(stderr, " nostack crash with a NULL stack pointer\n");
|
||||
fprintf(stderr, " ctest (obsoleted by thread-crash?)\n");
|
||||
fprintf(stderr, " exit call exit(1)\n");
|
||||
fprintf(stderr, " abort call abort()\n");
|
||||
fprintf(stderr, " assert call assert() without a function\n");
|
||||
fprintf(stderr, " assert2 call assert() with a function\n");
|
||||
fprintf(stderr, " LOG_ALWAYS_FATAL call LOG_ALWAYS_FATAL\n");
|
||||
fprintf(stderr, " LOG_ALWAYS_FATAL_IF call LOG_ALWAYS_FATAL\n");
|
||||
fprintf(stderr, " SIGFPE cause a SIGFPE\n");
|
||||
fprintf(stderr, " SIGSEGV cause a SIGSEGV at address 0x0 (synonym: crash)\n");
|
||||
fprintf(stderr, " SIGSEGV-non-null cause a SIGSEGV at a non-zero address\n");
|
||||
fprintf(stderr, " SIGSEGV-unmapped mmap/munmap a region of memory and then attempt to access it\n");
|
||||
fprintf(stderr, " SIGTRAP cause a SIGTRAP\n");
|
||||
fprintf(stderr, "prefix any of the above with 'thread-' to not run\n");
|
||||
fprintf(stderr, "on the process' main thread.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
fprintf(stderr,"crasher: built at " __TIME__ "!@\n");
|
||||
|
||||
if(argc > 1) {
|
||||
return do_action(argv[1]);
|
||||
} else {
|
||||
crash1();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,950 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <selinux/android.h>
|
||||
|
||||
#include <log/logger.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/debugger.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <nativehelper/ScopedFd.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "getevent.h"
|
||||
#include "signal_sender.h"
|
||||
#include "tombstone.h"
|
||||
#include "utility.h"
|
||||
|
||||
// If the 32 bit executable is compiled on a 64 bit system,
|
||||
// use the 32 bit socket name.
|
||||
#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
|
||||
#define SOCKET_NAME DEBUGGER32_SOCKET_NAME
|
||||
#else
|
||||
#define SOCKET_NAME DEBUGGER_SOCKET_NAME
|
||||
#endif
|
||||
|
||||
struct debugger_request_t {
|
||||
debugger_action_t action;
|
||||
pid_t pid, tid;
|
||||
uid_t uid, gid;
|
||||
uintptr_t abort_msg_address;
|
||||
int32_t original_si_code;
|
||||
};
|
||||
|
||||
static void wait_for_user_action(const debugger_request_t& request) {
|
||||
// Explain how to attach the debugger.
|
||||
ALOGI("***********************************************************\n"
|
||||
"* Process %d has been suspended while crashing.\n"
|
||||
"* To attach gdbserver and start gdb, run this on the host:\n"
|
||||
"*\n"
|
||||
"* gdbclient.py -p %d\n"
|
||||
"*\n"
|
||||
"* Wait for gdb to start, then press the VOLUME DOWN key\n"
|
||||
"* to let the process continue crashing.\n"
|
||||
"***********************************************************",
|
||||
request.pid, request.tid);
|
||||
|
||||
// Wait for VOLUME DOWN.
|
||||
while (true) {
|
||||
input_event e;
|
||||
if (get_event(&e, -1) == 0) {
|
||||
if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALOGI("debuggerd resuming process %d", request.pid);
|
||||
}
|
||||
|
||||
static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) {
|
||||
char path[64];
|
||||
snprintf(path, sizeof(path), "/proc/%d/status", tid);
|
||||
|
||||
FILE* fp = fopen(path, "r");
|
||||
if (!fp) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fields = 0;
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
size_t len = strlen(line);
|
||||
if (len > 6 && !memcmp(line, "Tgid:\t", 6)) {
|
||||
*out_pid = atoi(line + 6);
|
||||
fields |= 1;
|
||||
} else if (len > 5 && !memcmp(line, "Uid:\t", 5)) {
|
||||
*out_uid = atoi(line + 5);
|
||||
fields |= 2;
|
||||
} else if (len > 5 && !memcmp(line, "Gid:\t", 5)) {
|
||||
*out_gid = atoi(line + 5);
|
||||
fields |= 4;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return fields == 7 ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Corresponds with debugger_action_t enum type in
|
||||
* include/cutils/debugger.h.
|
||||
*/
|
||||
static const char *debuggerd_perms[] = {
|
||||
NULL, /* crash is only used on self, no check applied */
|
||||
"dump_tombstone",
|
||||
"dump_backtrace"
|
||||
};
|
||||
|
||||
static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len)
|
||||
{
|
||||
struct debugger_request_t* req = reinterpret_cast<debugger_request_t*>(data);
|
||||
|
||||
if (!req) {
|
||||
ALOGE("No debuggerd request audit data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(buf, len, "pid=%d uid=%d gid=%d", req->pid, req->uid, req->gid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool selinux_action_allowed(int s, debugger_request_t* request)
|
||||
{
|
||||
char *scon = NULL, *tcon = NULL;
|
||||
const char *tclass = "debuggerd";
|
||||
const char *perm;
|
||||
bool allowed = false;
|
||||
|
||||
if (request->action <= 0 || request->action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
|
||||
ALOGE("SELinux: No permission defined for debugger action %d", request->action);
|
||||
return false;
|
||||
}
|
||||
|
||||
perm = debuggerd_perms[request->action];
|
||||
|
||||
if (getpeercon(s, &scon) < 0) {
|
||||
ALOGE("Cannot get peer context from socket\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (getpidcon(request->tid, &tcon) < 0) {
|
||||
ALOGE("Cannot get context for tid %d\n", request->tid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
allowed = true; // (selinux_check_access(scon, tcon, tclass, perm, reinterpret_cast<void*>(request)) == 0);
|
||||
|
||||
out:
|
||||
freecon(scon);
|
||||
freecon(tcon);
|
||||
return allowed;
|
||||
}
|
||||
|
||||
static bool pid_contains_tid(pid_t pid, pid_t tid) {
|
||||
char task_path[PATH_MAX];
|
||||
if (snprintf(task_path, PATH_MAX, "/proc/%d/task/%d", pid, tid) >= PATH_MAX) {
|
||||
ALOGE("debuggerd: task path overflow (pid = %d, tid = %d)\n", pid, tid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return access(task_path, F_OK) == 0;
|
||||
}
|
||||
|
||||
static int read_request(int fd, debugger_request_t* out_request) {
|
||||
ucred cr;
|
||||
socklen_t len = sizeof(cr);
|
||||
int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
|
||||
if (status != 0) {
|
||||
ALOGE("cannot get credentials");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ALOGV("reading tid");
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
pollfd pollfds[1];
|
||||
pollfds[0].fd = fd;
|
||||
pollfds[0].events = POLLIN;
|
||||
pollfds[0].revents = 0;
|
||||
status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));
|
||||
if (status != 1) {
|
||||
ALOGE("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debugger_msg_t msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
|
||||
if (status < 0) {
|
||||
ALOGE("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid);
|
||||
return -1;
|
||||
}
|
||||
if (status != sizeof(debugger_msg_t)) {
|
||||
ALOGE("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
out_request->action = static_cast<debugger_action_t>(msg.action);
|
||||
out_request->tid = msg.tid;
|
||||
out_request->pid = cr.pid;
|
||||
out_request->uid = cr.uid;
|
||||
out_request->gid = cr.gid;
|
||||
out_request->abort_msg_address = msg.abort_msg_address;
|
||||
out_request->original_si_code = msg.original_si_code;
|
||||
|
||||
if (msg.action == DEBUGGER_ACTION_CRASH) {
|
||||
// Ensure that the tid reported by the crashing process is valid.
|
||||
// This check needs to happen again after ptracing the requested thread to prevent a race.
|
||||
if (!pid_contains_tid(out_request->pid, out_request->tid)) {
|
||||
ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid,
|
||||
out_request->pid);
|
||||
return -1;
|
||||
}
|
||||
} else if (cr.uid == 0 || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
|
||||
// Only root or system can ask us to attach to any process and dump it explicitly.
|
||||
// However, system is only allowed to collect backtraces but cannot dump tombstones.
|
||||
status = get_process_info(out_request->tid, &out_request->pid,
|
||||
&out_request->uid, &out_request->gid);
|
||||
if (status < 0) {
|
||||
ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!selinux_action_allowed(fd, out_request))
|
||||
return -1;
|
||||
} else {
|
||||
// No one else is allowed to dump arbitrary processes.
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int activity_manager_connect() {
|
||||
android::base::unique_fd amfd(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
if (amfd.get() < -1) {
|
||||
ALOGE("debuggerd: Unable to connect to activity manager (socket failed: %s)", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_un address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
address.sun_family = AF_UNIX;
|
||||
// The path used here must match the value defined in NativeCrashListener.java.
|
||||
strncpy(address.sun_path, "/data/system/ndebugsocket", sizeof(address.sun_path));
|
||||
if (TEMP_FAILURE_RETRY(connect(amfd.get(), reinterpret_cast<struct sockaddr*>(&address),
|
||||
sizeof(address))) == -1) {
|
||||
ALOGE("debuggerd: Unable to connect to activity manager (connect failed: %s)", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
memset(&tv, 0, sizeof(tv));
|
||||
tv.tv_sec = 1; // tight leash
|
||||
if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
|
||||
ALOGE("debuggerd: Unable to connect to activity manager (setsockopt SO_SNDTIMEO failed: %s)",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
tv.tv_sec = 3; // 3 seconds on handshake read
|
||||
if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
|
||||
ALOGE("debuggerd: Unable to connect to activity manager (setsockopt SO_RCVTIMEO failed: %s)",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return amfd.release();
|
||||
}
|
||||
|
||||
static void activity_manager_write(int pid, int signal, int amfd, const std::string& amfd_data) {
|
||||
if (amfd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Activity Manager protocol: binary 32-bit network-byte-order ints for the
|
||||
// pid and signal number, followed by the raw text of the dump, culminating
|
||||
// in a zero byte that marks end-of-data.
|
||||
uint32_t datum = htonl(pid);
|
||||
if (!android::base::WriteFully(amfd, &datum, 4)) {
|
||||
ALOGE("AM pid write failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
datum = htonl(signal);
|
||||
if (!android::base::WriteFully(amfd, &datum, 4)) {
|
||||
ALOGE("AM signal write failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size())) {
|
||||
ALOGE("AM data write failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send EOD to the Activity Manager, then wait for its ack to avoid racing
|
||||
// ahead and killing the target out from under it.
|
||||
uint8_t eodMarker = 0;
|
||||
if (!android::base::WriteFully(amfd, &eodMarker, 1)) {
|
||||
ALOGE("AM eod write failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
// 3 sec timeout reading the ack; we're fine if the read fails.
|
||||
android::base::ReadFully(amfd, &eodMarker, 1);
|
||||
}
|
||||
|
||||
static bool should_attach_gdb(const debugger_request_t& request) {
|
||||
if (request.action == DEBUGGER_ACTION_CRASH) {
|
||||
return property_get_bool("debug.debuggerd.wait_for_gdb", false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(__LP64__)
|
||||
static bool is32bit(pid_t tid) {
|
||||
char* exeline;
|
||||
if (asprintf(&exeline, "/proc/%d/exe", tid) == -1) {
|
||||
return false;
|
||||
}
|
||||
int fd = TEMP_FAILURE_RETRY(open(exeline, O_RDONLY | O_CLOEXEC));
|
||||
int saved_errno = errno;
|
||||
free(exeline);
|
||||
if (fd == -1) {
|
||||
ALOGW("Failed to open /proc/%d/exe %s", tid, strerror(saved_errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
char ehdr[EI_NIDENT];
|
||||
ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr)));
|
||||
close(fd);
|
||||
if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (ehdr[EI_CLASS] == ELFCLASS32) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void redirect_to_32(int fd, debugger_request_t* request) {
|
||||
debugger_msg_t msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.tid = request->tid;
|
||||
msg.action = request->action;
|
||||
|
||||
int sock_fd = socket_local_client(DEBUGGER32_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
|
||||
SOCK_STREAM | SOCK_CLOEXEC);
|
||||
if (sock_fd < 0) {
|
||||
ALOGE("Failed to connect to debuggerd32: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) {
|
||||
ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno));
|
||||
close(sock_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
char ack;
|
||||
if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) {
|
||||
ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno));
|
||||
close(sock_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
ssize_t bytes_read;
|
||||
while ((bytes_read = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
|
||||
ssize_t bytes_to_send = bytes_read;
|
||||
ssize_t bytes_written;
|
||||
do {
|
||||
bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer + bytes_read - bytes_to_send,
|
||||
bytes_to_send));
|
||||
if (bytes_written == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
// Retry the write.
|
||||
continue;
|
||||
}
|
||||
ALOGE("Error while writing data to fd: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
bytes_to_send -= bytes_written;
|
||||
} while (bytes_written != 0 && bytes_to_send > 0);
|
||||
if (bytes_to_send != 0) {
|
||||
ALOGE("Failed to write all data to fd: read %zd, sent %zd", bytes_read, bytes_to_send);
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(sock_fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Attach to a thread, and verify that it's still a member of the given process
|
||||
static bool ptrace_attach_thread(pid_t pid, pid_t tid) {
|
||||
if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure that the task we attached to is actually part of the pid we're dumping.
|
||||
if (!pid_contains_tid(pid, tid)) {
|
||||
if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
|
||||
ALOGE("debuggerd: failed to detach from thread '%d'", tid);
|
||||
exit(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) {
|
||||
char task_path[PATH_MAX];
|
||||
|
||||
if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) {
|
||||
ALOGE("debuggerd: task path overflow (pid = %d)\n", pid);
|
||||
abort();
|
||||
}
|
||||
|
||||
std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path), closedir);
|
||||
|
||||
// Bail early if the task directory cannot be opened.
|
||||
if (!d) {
|
||||
ALOGE("debuggerd: failed to open /proc/%d/task: %s", pid, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent* de;
|
||||
while ((de = readdir(d.get())) != NULL) {
|
||||
// Ignore "." and "..".
|
||||
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char* end;
|
||||
pid_t tid = strtoul(de->d_name, &end, 10);
|
||||
if (*end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tid == main_tid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ptrace_attach_thread(pid, tid)) {
|
||||
ALOGE("debuggerd: ptrace attach to %d failed: %s", tid, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
tids.insert(tid);
|
||||
}
|
||||
}
|
||||
|
||||
static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
|
||||
BacktraceMap* backtrace_map, const std::set<pid_t>& siblings,
|
||||
int* crash_signal, std::string* amfd_data) {
|
||||
if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
|
||||
ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
int total_sleep_time_usec = 0;
|
||||
while (true) {
|
||||
int signal = wait_for_signal(request.tid, &total_sleep_time_usec);
|
||||
switch (signal) {
|
||||
case -1:
|
||||
ALOGE("debuggerd: timed out waiting for signal");
|
||||
return false;
|
||||
|
||||
case SIGSTOP:
|
||||
if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
|
||||
ALOGV("debuggerd: stopped -- dumping to tombstone");
|
||||
engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
|
||||
request.original_si_code, request.abort_msg_address, amfd_data);
|
||||
} else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
|
||||
ALOGV("debuggerd: stopped -- dumping to fd");
|
||||
dump_backtrace(fd, backtrace_map, request.pid, request.tid, siblings, nullptr);
|
||||
} else {
|
||||
ALOGV("debuggerd: stopped -- continuing");
|
||||
if (ptrace(PTRACE_CONT, request.tid, 0, 0) != 0) {
|
||||
ALOGE("debuggerd: ptrace continue failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
continue; // loop again
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGABRT:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGILL:
|
||||
case SIGSEGV:
|
||||
#ifdef SIGSTKFLT
|
||||
case SIGSTKFLT:
|
||||
#endif
|
||||
case SIGSYS:
|
||||
case SIGTRAP:
|
||||
ALOGV("stopped -- fatal signal\n");
|
||||
*crash_signal = signal;
|
||||
engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
|
||||
request.original_si_code, request.abort_msg_address, amfd_data);
|
||||
break;
|
||||
|
||||
default:
|
||||
ALOGE("debuggerd: process stopped due to unexpected signal %d\n", signal);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool drop_privileges() {
|
||||
// AID_LOG: for reading the logs data associated with the crashing process.
|
||||
// AID_READPROC: for reading /proc/<PID>/{comm,cmdline}.
|
||||
gid_t groups[] = { AID_DEBUGGERD, AID_LOG, AID_READPROC };
|
||||
if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
|
||||
ALOGE("debuggerd: failed to setgroups: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setresgid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
|
||||
ALOGE("debuggerd: failed to setresgid: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
|
||||
ALOGE("debuggerd: failed to setresuid: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void worker_process(int fd, debugger_request_t& request) {
|
||||
// Open the tombstone file if we need it.
|
||||
std::string tombstone_path;
|
||||
int tombstone_fd = -1;
|
||||
switch (request.action) {
|
||||
case DEBUGGER_ACTION_DUMP_TOMBSTONE:
|
||||
case DEBUGGER_ACTION_CRASH:
|
||||
tombstone_fd = open_tombstone(&tombstone_path);
|
||||
if (tombstone_fd == -1) {
|
||||
ALOGE("debuggerd: failed to open tombstone file: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case DEBUGGER_ACTION_DUMP_BACKTRACE:
|
||||
break;
|
||||
|
||||
default:
|
||||
ALOGE("debuggerd: unexpected request action: %d", request.action);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// At this point, the thread that made the request is blocked in
|
||||
// a read() call. If the thread has crashed, then this gives us
|
||||
// time to PTRACE_ATTACH to it before it has a chance to really fault.
|
||||
//
|
||||
// The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
|
||||
// won't necessarily have stopped by the time ptrace() returns. (We
|
||||
// currently assume it does.) We write to the file descriptor to
|
||||
// ensure that it can run as soon as we call PTRACE_CONT below.
|
||||
// See details in bionic/libc/linker/debugger.c, in function
|
||||
// debugger_signal_handler().
|
||||
|
||||
// Attach to the target process.
|
||||
if (!ptrace_attach_thread(request.pid, request.tid)) {
|
||||
ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in the
|
||||
// request is sent from the other side. If an attacker can cause a process to be spawned with the
|
||||
// pid of their process, they could trick debuggerd into dumping that process by exiting after
|
||||
// sending the request. Validate the trusted request.uid/gid to defend against this.
|
||||
if (request.action == DEBUGGER_ACTION_CRASH) {
|
||||
pid_t pid;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
if (get_process_info(request.tid, &pid, &uid, &gid) != 0) {
|
||||
ALOGE("debuggerd: failed to get process info for tid '%d'", request.tid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pid != request.pid || uid != request.uid || gid != request.gid) {
|
||||
ALOGE(
|
||||
"debuggerd: attached task %d does not match request: "
|
||||
"expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d",
|
||||
request.tid, request.pid, request.uid, request.gid, pid, uid, gid);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't attach to the sibling threads if we want to attach gdb.
|
||||
// Supposedly, it makes the process less reliable.
|
||||
bool attach_gdb = should_attach_gdb(request);
|
||||
if (attach_gdb) {
|
||||
// Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
|
||||
if (init_getevent() != 0) {
|
||||
ALOGE("debuggerd: failed to initialize input device, not waiting for gdb");
|
||||
attach_gdb = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::set<pid_t> siblings;
|
||||
if (!attach_gdb) {
|
||||
ptrace_siblings(request.pid, request.tid, siblings);
|
||||
}
|
||||
|
||||
// Generate the backtrace map before dropping privileges.
|
||||
std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
|
||||
|
||||
int amfd = -1;
|
||||
std::unique_ptr<std::string> amfd_data;
|
||||
if (request.action == DEBUGGER_ACTION_CRASH) {
|
||||
// Connect to the activity manager before dropping privileges.
|
||||
amfd = activity_manager_connect();
|
||||
amfd_data.reset(new std::string);
|
||||
}
|
||||
|
||||
bool succeeded = false;
|
||||
|
||||
// Now that we've done everything that requires privileges, we can drop them.
|
||||
if (!drop_privileges()) {
|
||||
ALOGE("debuggerd: failed to drop privileges, exiting");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
int crash_signal = SIGKILL;
|
||||
succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings,
|
||||
&crash_signal, amfd_data.get());
|
||||
if (succeeded) {
|
||||
if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
|
||||
if (!tombstone_path.empty()) {
|
||||
android::base::WriteFully(fd, tombstone_path.c_str(), tombstone_path.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attach_gdb) {
|
||||
// Tell the signal process to send SIGSTOP to the target.
|
||||
if (!send_signal(request.pid, 0, SIGSTOP)) {
|
||||
ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno));
|
||||
attach_gdb = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!attach_gdb) {
|
||||
// Tell the Activity Manager about the crashing process. If we are
|
||||
// waiting for gdb to attach, do not send this or Activity Manager
|
||||
// might kill the process before anyone can attach.
|
||||
activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_DETACH, request.tid, 0, 0) != 0) {
|
||||
ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
|
||||
}
|
||||
|
||||
for (pid_t sibling : siblings) {
|
||||
ptrace(PTRACE_DETACH, sibling, 0, 0);
|
||||
}
|
||||
|
||||
// Send the signal back to the process if it crashed and we're not waiting for gdb.
|
||||
if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
|
||||
if (!send_signal(request.pid, request.tid, crash_signal)) {
|
||||
ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for gdb, if requested.
|
||||
if (attach_gdb) {
|
||||
wait_for_user_action(request);
|
||||
|
||||
// Now tell the activity manager about this process.
|
||||
activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
|
||||
|
||||
// Tell the signal process to send SIGCONT to the target.
|
||||
if (!send_signal(request.pid, 0, SIGCONT)) {
|
||||
ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno));
|
||||
}
|
||||
|
||||
uninit_getevent();
|
||||
}
|
||||
|
||||
close(amfd);
|
||||
|
||||
exit(!succeeded);
|
||||
}
|
||||
|
||||
static void monitor_worker_process(int child_pid, const debugger_request_t& request) {
|
||||
struct timespec timeout = {.tv_sec = 10, .tv_nsec = 0 };
|
||||
if (should_attach_gdb(request)) {
|
||||
// If wait_for_gdb is enabled, set the timeout to something large.
|
||||
timeout.tv_sec = INT_MAX;
|
||||
}
|
||||
|
||||
sigset_t signal_set;
|
||||
sigemptyset(&signal_set);
|
||||
sigaddset(&signal_set, SIGCHLD);
|
||||
|
||||
bool kill_worker = false;
|
||||
bool kill_target = false;
|
||||
bool kill_self = false;
|
||||
|
||||
int status;
|
||||
siginfo_t siginfo;
|
||||
int signal = TEMP_FAILURE_RETRY(sigtimedwait(&signal_set, &siginfo, &timeout));
|
||||
if (signal == SIGCHLD) {
|
||||
pid_t rc = waitpid(-1, &status, WNOHANG | WUNTRACED);
|
||||
if (rc != child_pid) {
|
||||
ALOGE("debuggerd: waitpid returned unexpected pid (%d), committing murder-suicide", rc);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
ALOGW("debuggerd: pid %d exited with status %d", rc, WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
ALOGW("debuggerd: pid %d received signal %d", rc, WTERMSIG(status));
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
ALOGW("debuggerd: pid %d stopped by signal %d", rc, WSTOPSIG(status));
|
||||
} else if (WIFCONTINUED(status)) {
|
||||
ALOGW("debuggerd: pid %d continued", rc);
|
||||
}
|
||||
|
||||
kill_worker = true;
|
||||
kill_target = true;
|
||||
kill_self = true;
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
ALOGE("debuggerd: worker process %d terminated due to signal %d", child_pid, WTERMSIG(status));
|
||||
kill_worker = false;
|
||||
kill_target = true;
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
ALOGE("debuggerd: worker process %d stopped due to signal %d", child_pid, WSTOPSIG(status));
|
||||
kill_worker = true;
|
||||
kill_target = true;
|
||||
}
|
||||
} else {
|
||||
ALOGE("debuggerd: worker process %d timed out", child_pid);
|
||||
kill_worker = true;
|
||||
kill_target = true;
|
||||
}
|
||||
|
||||
if (kill_worker) {
|
||||
// Something bad happened, kill the worker.
|
||||
if (kill(child_pid, SIGKILL) != 0) {
|
||||
ALOGE("debuggerd: failed to kill worker process %d: %s", child_pid, strerror(errno));
|
||||
} else {
|
||||
waitpid(child_pid, &status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int exit_signal = SIGCONT;
|
||||
if (kill_target && request.action == DEBUGGER_ACTION_CRASH) {
|
||||
ALOGE("debuggerd: killing target %d", request.pid);
|
||||
exit_signal = SIGKILL;
|
||||
} else {
|
||||
ALOGW("debuggerd: resuming target %d", request.pid);
|
||||
}
|
||||
|
||||
if (kill(request.pid, exit_signal) != 0) {
|
||||
ALOGE("debuggerd: failed to send signal %d to target: %s", exit_signal, strerror(errno));
|
||||
}
|
||||
|
||||
if (kill_self) {
|
||||
stop_signal_sender();
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_request(int fd) {
|
||||
ALOGV("handle_request(%d)\n", fd);
|
||||
|
||||
ScopedFd closer(fd);
|
||||
debugger_request_t request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
int status = read_request(fd, &request);
|
||||
if (status != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ALOGW("debuggerd: handling request: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid,
|
||||
request.gid, request.tid);
|
||||
|
||||
#if defined(__LP64__)
|
||||
// On 64 bit systems, requests to dump 32 bit and 64 bit tids come
|
||||
// to the 64 bit debuggerd. If the process is a 32 bit executable,
|
||||
// redirect the request to the 32 bit debuggerd.
|
||||
if (is32bit(request.tid)) {
|
||||
// Only dump backtrace and dump tombstone requests can be redirected.
|
||||
if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
|
||||
request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
|
||||
redirect_to_32(fd, &request);
|
||||
} else {
|
||||
ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fork a child to handle the rest of the request.
|
||||
pid_t fork_pid = fork();
|
||||
if (fork_pid == -1) {
|
||||
ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
|
||||
} else if (fork_pid == 0) {
|
||||
worker_process(fd, request);
|
||||
} else {
|
||||
monitor_worker_process(fork_pid, request);
|
||||
}
|
||||
}
|
||||
|
||||
static int do_server() {
|
||||
// debuggerd crashes can't be reported to debuggerd.
|
||||
// Reset all of the crash handlers.
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
signal(SIGBUS, SIG_DFL);
|
||||
signal(SIGFPE, SIG_DFL);
|
||||
signal(SIGILL, SIG_DFL);
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
#ifdef SIGSTKFLT
|
||||
signal(SIGSTKFLT, SIG_DFL);
|
||||
#endif
|
||||
signal(SIGTRAP, SIG_DFL);
|
||||
|
||||
// Ignore failed writes to closed sockets
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
// Block SIGCHLD so we can sigtimedwait for it.
|
||||
sigset_t sigchld;
|
||||
sigemptyset(&sigchld);
|
||||
sigaddset(&sigchld, SIGCHLD);
|
||||
sigprocmask(SIG_SETMASK, &sigchld, nullptr);
|
||||
|
||||
int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
|
||||
SOCK_STREAM | SOCK_CLOEXEC);
|
||||
if (s == -1) return 1;
|
||||
|
||||
// Fork a process that stays root, and listens on a pipe to pause and resume the target.
|
||||
if (!start_signal_sender()) {
|
||||
ALOGE("debuggerd: failed to fork signal sender");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ALOGI("debuggerd: starting\n");
|
||||
|
||||
for (;;) {
|
||||
sockaddr_storage ss;
|
||||
sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
|
||||
socklen_t alen = sizeof(ss);
|
||||
|
||||
ALOGV("waiting for connection\n");
|
||||
int fd = accept4(s, addrp, &alen, SOCK_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
ALOGE("accept failed: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
handle_request(fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_explicit_dump(pid_t tid, bool dump_backtrace) {
|
||||
fprintf(stdout, "Sending request to dump task %d.\n", tid);
|
||||
|
||||
if (dump_backtrace) {
|
||||
fflush(stdout);
|
||||
if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) {
|
||||
fputs("Error dumping backtrace.\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
char tombstone_path[PATH_MAX];
|
||||
if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) {
|
||||
fputs("Error dumping tombstone.\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage() {
|
||||
fputs("Usage: -b [<tid>]\n"
|
||||
" -b dump backtrace to console, otherwise dump full tombstone file\n"
|
||||
"\n"
|
||||
"If tid specified, sends a request to debuggerd to dump that task.\n"
|
||||
"Otherwise, starts the debuggerd server.\n", stderr);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
union selinux_callback cb;
|
||||
if (argc == 1) {
|
||||
cb.func_audit = audit_callback;
|
||||
selinux_set_callback(SELINUX_CB_AUDIT, cb);
|
||||
cb.func_log = selinux_log_callback;
|
||||
selinux_set_callback(SELINUX_CB_LOG, cb);
|
||||
return do_server();
|
||||
}
|
||||
|
||||
bool dump_backtrace = false;
|
||||
bool have_tid = false;
|
||||
pid_t tid = 0;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-b")) {
|
||||
dump_backtrace = true;
|
||||
} else if (!have_tid) {
|
||||
tid = atoi(argv[i]);
|
||||
have_tid = true;
|
||||
} else {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!have_tid) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
return do_explicit_dump(tid, dump_backtrace);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
service debuggerd /system/bin/debuggerd
|
||||
group root readproc
|
||||
writepid /dev/cpuset/system-background/tasks
|
||||
@@ -1,3 +0,0 @@
|
||||
service debuggerd64 /system/bin/debuggerd64
|
||||
group root readproc
|
||||
writepid /dev/cpuset/system-background/tasks
|
||||
@@ -1,121 +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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "DEBUG"
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include "elf_utils.h"
|
||||
|
||||
#define NOTE_ALIGN(size) ((size + 3) & ~3)
|
||||
|
||||
template <typename HdrType, typename PhdrType, typename NhdrType>
|
||||
static bool get_build_id(
|
||||
Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
|
||||
HdrType hdr;
|
||||
|
||||
memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
|
||||
|
||||
// First read the rest of the header.
|
||||
if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
|
||||
sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < hdr.e_phnum; i++) {
|
||||
PhdrType phdr;
|
||||
if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
|
||||
reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
|
||||
return false;
|
||||
}
|
||||
// Looking for the .note.gnu.build-id note.
|
||||
if (phdr.p_type == PT_NOTE) {
|
||||
size_t hdr_size = phdr.p_filesz;
|
||||
uintptr_t addr = base_addr + phdr.p_offset;
|
||||
while (hdr_size >= sizeof(NhdrType)) {
|
||||
NhdrType nhdr;
|
||||
if (backtrace->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
|
||||
return false;
|
||||
}
|
||||
addr += sizeof(nhdr);
|
||||
if (nhdr.n_type == NT_GNU_BUILD_ID) {
|
||||
// Skip the name (which is the owner and should be "GNU").
|
||||
addr += NOTE_ALIGN(nhdr.n_namesz);
|
||||
uint8_t build_id_data[160];
|
||||
if (nhdr.n_descsz > sizeof(build_id_data)) {
|
||||
ALOGE("Possible corrupted note, desc size value is too large: %u",
|
||||
nhdr.n_descsz);
|
||||
return false;
|
||||
}
|
||||
if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
|
||||
return false;
|
||||
}
|
||||
|
||||
build_id->clear();
|
||||
for (size_t bytes = 0; bytes < nhdr.n_descsz; bytes++) {
|
||||
*build_id += android::base::StringPrintf("%02x", build_id_data[bytes]);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Move past the extra note data.
|
||||
hdr_size -= sizeof(nhdr);
|
||||
size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
|
||||
addr += skip_bytes;
|
||||
if (hdr_size < skip_bytes) {
|
||||
break;
|
||||
}
|
||||
hdr_size -= skip_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) {
|
||||
// Read and verify the elf magic number first.
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the rest of EI_NIDENT.
|
||||
if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e_ident[EI_CLASS] == ELFCLASS32) {
|
||||
return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(backtrace, addr, e_ident, build_id);
|
||||
} else if (e_ident[EI_CLASS] == ELFCLASS64) {
|
||||
return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(backtrace, addr, e_ident, build_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,27 +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.
|
||||
*/
|
||||
|
||||
#ifndef _DEBUGGERD_ELF_UTILS_H
|
||||
#define _DEBUGGERD_ELF_UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
class Backtrace;
|
||||
|
||||
bool elf_get_build_id(Backtrace*, uintptr_t, std::string*);
|
||||
|
||||
#endif // _DEBUGGERD_ELF_UTILS_H
|
||||
@@ -1,222 +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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/poll.h>
|
||||
#include <linux/input.h>
|
||||
#include <errno.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
static struct pollfd* ufds;
|
||||
static char** device_names;
|
||||
static int nfds;
|
||||
|
||||
static int open_device(const char* device) {
|
||||
int version;
|
||||
int fd;
|
||||
struct pollfd* new_ufds;
|
||||
char** new_device_names;
|
||||
char name[80];
|
||||
char location[80];
|
||||
char idstr[80];
|
||||
struct input_id id;
|
||||
|
||||
fd = open(device, O_RDWR);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, EVIOCGVERSION, &version)) {
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, EVIOCGID, &id)) {
|
||||
return -1;
|
||||
}
|
||||
name[sizeof(name) - 1] = '\0';
|
||||
location[sizeof(location) - 1] = '\0';
|
||||
idstr[sizeof(idstr) - 1] = '\0';
|
||||
if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
|
||||
name[0] = '\0';
|
||||
}
|
||||
if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
|
||||
location[0] = '\0';
|
||||
}
|
||||
if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
|
||||
idstr[0] = '\0';
|
||||
}
|
||||
|
||||
new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1)));
|
||||
if (new_ufds == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
ufds = new_ufds;
|
||||
new_device_names = reinterpret_cast<char**>(realloc(
|
||||
device_names, sizeof(device_names[0]) * (nfds + 1)));
|
||||
if (new_device_names == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
device_names = new_device_names;
|
||||
ufds[nfds].fd = fd;
|
||||
ufds[nfds].events = POLLIN;
|
||||
device_names[nfds] = strdup(device);
|
||||
nfds++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int close_device(const char* device) {
|
||||
int i;
|
||||
for (i = 1; i < nfds; i++) {
|
||||
if (strcmp(device_names[i], device) == 0) {
|
||||
int count = nfds - i - 1;
|
||||
free(device_names[i]);
|
||||
memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
|
||||
memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
|
||||
nfds--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int read_notify(const char* dirname, int nfd) {
|
||||
int res;
|
||||
char devname[PATH_MAX];
|
||||
char* filename;
|
||||
char event_buf[512];
|
||||
int event_size;
|
||||
int event_pos = 0;
|
||||
struct inotify_event *event;
|
||||
|
||||
res = read(nfd, event_buf, sizeof(event_buf));
|
||||
if (res < (int)sizeof(*event)) {
|
||||
if (errno == EINTR)
|
||||
return 0;
|
||||
fprintf(stderr, "could not get event, %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
strcpy(devname, dirname);
|
||||
filename = devname + strlen(devname);
|
||||
*filename++ = '/';
|
||||
|
||||
while (res >= (int)sizeof(*event)) {
|
||||
event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
|
||||
if (event->len) {
|
||||
strcpy(filename, event->name);
|
||||
if (event->mask & IN_CREATE) {
|
||||
open_device(devname);
|
||||
} else {
|
||||
close_device(devname);
|
||||
}
|
||||
}
|
||||
event_size = sizeof(*event) + event->len;
|
||||
res -= event_size;
|
||||
event_pos += event_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_dir(const char* dirname) {
|
||||
char devname[PATH_MAX];
|
||||
char* filename;
|
||||
DIR* dir;
|
||||
struct dirent* de;
|
||||
dir = opendir(dirname);
|
||||
if (dir == NULL)
|
||||
return -1;
|
||||
strcpy(devname, dirname);
|
||||
filename = devname + strlen(devname);
|
||||
*filename++ = '/';
|
||||
while ((de = readdir(dir))) {
|
||||
if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
|
||||
(de->d_name[1] == '.' && de->d_name[2] == '\0'))
|
||||
continue;
|
||||
strcpy(filename, de->d_name);
|
||||
open_device(devname);
|
||||
}
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_getevent() {
|
||||
int res;
|
||||
const char* device_path = "/dev/input";
|
||||
|
||||
nfds = 1;
|
||||
ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0])));
|
||||
ufds[0].fd = inotify_init();
|
||||
ufds[0].events = POLLIN;
|
||||
|
||||
res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
|
||||
if (res < 0) {
|
||||
return 1;
|
||||
}
|
||||
res = scan_dir(device_path);
|
||||
if (res < 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uninit_getevent() {
|
||||
int i;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
close(ufds[i].fd);
|
||||
}
|
||||
free(ufds);
|
||||
ufds = 0;
|
||||
nfds = 0;
|
||||
}
|
||||
|
||||
int get_event(struct input_event* event, int timeout) {
|
||||
int res;
|
||||
int i;
|
||||
int pollres;
|
||||
const char* device_path = "/dev/input";
|
||||
while (1) {
|
||||
pollres = poll(ufds, nfds, timeout);
|
||||
if (pollres == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (ufds[0].revents & POLLIN) {
|
||||
read_notify(device_path, ufds[0].fd);
|
||||
}
|
||||
for (i = 1; i < nfds; i++) {
|
||||
if (ufds[i].revents) {
|
||||
if (ufds[i].revents & POLLIN) {
|
||||
res = read(ufds[i].fd, event, sizeof(*event));
|
||||
if (res < static_cast<int>(sizeof(event))) {
|
||||
fprintf(stderr, "could not get event\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 _DEBUGGERD_GETEVENT_H
|
||||
#define _DEBUGGERD_GETEVENT_H
|
||||
|
||||
int init_getevent();
|
||||
void uninit_getevent();
|
||||
int get_event(struct input_event* event, int timeout);
|
||||
|
||||
#endif // _DEBUGGERD_GETEVENT_H
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 _DEBUGGERD_MACHINE_H
|
||||
#define _DEBUGGERD_MACHINE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
void dump_memory_and_code(log_t* log, Backtrace* backtrace);
|
||||
void dump_registers(log_t* log, pid_t tid);
|
||||
|
||||
#endif // _DEBUGGERD_MACHINE_H
|
||||
@@ -1,45 +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 <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "sys/system_properties.h"
|
||||
|
||||
std::unordered_map<std::string, std::string> g_properties;
|
||||
|
||||
extern "C" int property_set(const char* name, const char* value) {
|
||||
if (g_properties.count(name) != 0) {
|
||||
g_properties.erase(name);
|
||||
}
|
||||
g_properties[name] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int property_get(const char* key, char* value, const char* default_value) {
|
||||
if (g_properties.count(key) == 0) {
|
||||
if (default_value == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
strncpy(value, default_value, PROP_VALUE_MAX-1);
|
||||
} else {
|
||||
strncpy(value, g_properties[key].c_str(), PROP_VALUE_MAX-1);
|
||||
}
|
||||
value[PROP_VALUE_MAX-1] = '\0';
|
||||
return strlen(value);
|
||||
}
|
||||
@@ -1,17 +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.
|
||||
*/
|
||||
|
||||
extern "C" int selinux_android_restorecon(const char*, unsigned int);
|
||||
@@ -1,19 +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.
|
||||
*/
|
||||
|
||||
extern "C" int selinux_android_restorecon(const char*, unsigned int) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1,180 +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 <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <log/logger.h>
|
||||
|
||||
#include "signal_sender.h"
|
||||
|
||||
static int signal_fd = -1;
|
||||
static pid_t signal_pid;
|
||||
struct signal_message {
|
||||
pid_t pid;
|
||||
pid_t tid;
|
||||
int signal;
|
||||
};
|
||||
|
||||
static void set_signal_sender_process_name() {
|
||||
#if defined(__LP64__)
|
||||
static constexpr char long_process_name[] = "debuggerd64:signaller";
|
||||
static constexpr char short_process_name[] = "debuggerd64:sig";
|
||||
static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd64"), "");
|
||||
#else
|
||||
static constexpr char long_process_name[] = "debuggerd:signaller";
|
||||
static constexpr char short_process_name[] = "debuggerd:sig";
|
||||
static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd"), "");
|
||||
#endif
|
||||
|
||||
// pthread_setname_np has a maximum length of 16 chars, including null terminator.
|
||||
static_assert(sizeof(short_process_name) <= 16, "");
|
||||
pthread_setname_np(pthread_self(), short_process_name);
|
||||
|
||||
char* progname = const_cast<char*>(getprogname());
|
||||
if (strlen(progname) <= strlen(long_process_name)) {
|
||||
ALOGE("debuggerd: unexpected progname %s", progname);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(progname, 0, strlen(progname));
|
||||
strcpy(progname, long_process_name);
|
||||
}
|
||||
|
||||
// Fork a process to send signals for the worker processes to use after they've dropped privileges.
|
||||
bool start_signal_sender() {
|
||||
if (signal_pid != 0) {
|
||||
ALOGE("debuggerd: attempted to start signal sender multiple times");
|
||||
return false;
|
||||
}
|
||||
|
||||
int sfd[2];
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sfd) != 0) {
|
||||
ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t parent = getpid();
|
||||
pid_t fork_pid = fork();
|
||||
if (fork_pid == -1) {
|
||||
ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
|
||||
return false;
|
||||
} else if (fork_pid == 0) {
|
||||
close(sfd[1]);
|
||||
|
||||
set_signal_sender_process_name();
|
||||
|
||||
while (true) {
|
||||
signal_message msg;
|
||||
int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg)));
|
||||
if (rc < 0) {
|
||||
ALOGE("debuggerd: signal sender failed to read from socket");
|
||||
break;
|
||||
} else if (rc != sizeof(msg)) {
|
||||
ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
// Report success after sending a signal
|
||||
int err = 0;
|
||||
if (msg.tid > 0) {
|
||||
if (syscall(SYS_tgkill, msg.pid, msg.tid, msg.signal) != 0) {
|
||||
err = errno;
|
||||
}
|
||||
} else {
|
||||
if (kill(msg.pid, msg.signal) != 0) {
|
||||
err = errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) {
|
||||
ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
// Our parent proably died, but if not, kill them.
|
||||
if (getppid() == parent) {
|
||||
kill(parent, SIGKILL);
|
||||
}
|
||||
_exit(1);
|
||||
} else {
|
||||
close(sfd[0]);
|
||||
signal_fd = sfd[1];
|
||||
signal_pid = fork_pid;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool stop_signal_sender() {
|
||||
if (signal_pid <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kill(signal_pid, SIGKILL) != 0) {
|
||||
ALOGE("debuggerd: failed to kill signal sender: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
close(signal_fd);
|
||||
signal_fd = -1;
|
||||
|
||||
int status;
|
||||
waitpid(signal_pid, &status, 0);
|
||||
signal_pid = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_signal(pid_t pid, pid_t tid, int signal) {
|
||||
if (signal_fd == -1) {
|
||||
ALOGE("debuggerd: attempted to send signal before signal sender was started");
|
||||
errno = EHOSTUNREACH;
|
||||
return false;
|
||||
}
|
||||
|
||||
signal_message msg = {.pid = pid, .tid = tid, .signal = signal };
|
||||
if (TEMP_FAILURE_RETRY(write(signal_fd, &msg, sizeof(msg))) < 0) {
|
||||
ALOGE("debuggerd: failed to send message to signal sender: %s", strerror(errno));
|
||||
errno = EHOSTUNREACH;
|
||||
return false;
|
||||
}
|
||||
|
||||
int response;
|
||||
ssize_t rc = TEMP_FAILURE_RETRY(read(signal_fd, &response, sizeof(response)));
|
||||
if (rc == 0) {
|
||||
ALOGE("debuggerd: received EOF from signal sender");
|
||||
errno = EHOSTUNREACH;
|
||||
return false;
|
||||
} else if (rc < 0) {
|
||||
ALOGE("debuggerd: failed to receive response from signal sender: %s", strerror(errno));
|
||||
errno = EHOSTUNREACH;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
errno = response;
|
||||
return false;
|
||||
}
|
||||
@@ -1,30 +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.
|
||||
*/
|
||||
|
||||
#ifndef _DEBUGGERD_SIGNAL_SENDER_H
|
||||
#define _DEBUGGERD_SIGNAL_SENDER_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
bool start_signal_sender();
|
||||
bool stop_signal_sender();
|
||||
|
||||
// Sends a signal to a target process or thread.
|
||||
// If tid is greater than zero, this performs tgkill(pid, tid, signal).
|
||||
// Otherwise, it performs kill(pid, signal).
|
||||
bool send_signal(pid_t pid, pid_t tid, int signal);
|
||||
|
||||
#endif
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
|
||||
#define _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
|
||||
|
||||
// This is just enough to get the property code to compile on
|
||||
// the host.
|
||||
|
||||
#define PROP_NAME_MAX 32
|
||||
#define PROP_VALUE_MAX 92
|
||||
|
||||
#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
|
||||
@@ -1,702 +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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "DEBUG"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <log/log.h>
|
||||
#include <log/logger.h>
|
||||
#include <log/logprint.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include <selinux/android.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "elf_utils.h"
|
||||
#include "machine.h"
|
||||
#include "tombstone.h"
|
||||
|
||||
#define STACK_WORDS 16
|
||||
|
||||
#define MAX_TOMBSTONES 10
|
||||
#define TOMBSTONE_DIR "/data/tombstones"
|
||||
#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
|
||||
|
||||
static bool signal_has_si_addr(int sig) {
|
||||
switch (sig) {
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGILL:
|
||||
case SIGSEGV:
|
||||
case SIGTRAP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* get_signame(int sig) {
|
||||
switch(sig) {
|
||||
case SIGABRT: return "SIGABRT";
|
||||
case SIGBUS: return "SIGBUS";
|
||||
case SIGFPE: return "SIGFPE";
|
||||
case SIGILL: return "SIGILL";
|
||||
case SIGSEGV: return "SIGSEGV";
|
||||
#if defined(SIGSTKFLT)
|
||||
case SIGSTKFLT: return "SIGSTKFLT";
|
||||
#endif
|
||||
case SIGSTOP: return "SIGSTOP";
|
||||
case SIGTRAP: return "SIGTRAP";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* get_sigcode(int signo, int code) {
|
||||
// Try the signal-specific codes...
|
||||
switch (signo) {
|
||||
case SIGILL:
|
||||
switch (code) {
|
||||
case ILL_ILLOPC: return "ILL_ILLOPC";
|
||||
case ILL_ILLOPN: return "ILL_ILLOPN";
|
||||
case ILL_ILLADR: return "ILL_ILLADR";
|
||||
case ILL_ILLTRP: return "ILL_ILLTRP";
|
||||
case ILL_PRVOPC: return "ILL_PRVOPC";
|
||||
case ILL_PRVREG: return "ILL_PRVREG";
|
||||
case ILL_COPROC: return "ILL_COPROC";
|
||||
case ILL_BADSTK: return "ILL_BADSTK";
|
||||
}
|
||||
static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
|
||||
break;
|
||||
case SIGBUS:
|
||||
switch (code) {
|
||||
case BUS_ADRALN: return "BUS_ADRALN";
|
||||
case BUS_ADRERR: return "BUS_ADRERR";
|
||||
case BUS_OBJERR: return "BUS_OBJERR";
|
||||
case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
|
||||
case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
|
||||
}
|
||||
static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
|
||||
break;
|
||||
case SIGFPE:
|
||||
switch (code) {
|
||||
case FPE_INTDIV: return "FPE_INTDIV";
|
||||
case FPE_INTOVF: return "FPE_INTOVF";
|
||||
case FPE_FLTDIV: return "FPE_FLTDIV";
|
||||
case FPE_FLTOVF: return "FPE_FLTOVF";
|
||||
case FPE_FLTUND: return "FPE_FLTUND";
|
||||
case FPE_FLTRES: return "FPE_FLTRES";
|
||||
case FPE_FLTINV: return "FPE_FLTINV";
|
||||
case FPE_FLTSUB: return "FPE_FLTSUB";
|
||||
}
|
||||
static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
|
||||
break;
|
||||
case SIGSEGV:
|
||||
switch (code) {
|
||||
case SEGV_MAPERR: return "SEGV_MAPERR";
|
||||
case SEGV_ACCERR: return "SEGV_ACCERR";
|
||||
#if defined(SEGV_BNDERR)
|
||||
case SEGV_BNDERR: return "SEGV_BNDERR";
|
||||
#endif
|
||||
}
|
||||
#if defined(SEGV_BNDERR)
|
||||
static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
|
||||
#else
|
||||
static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
|
||||
#endif
|
||||
break;
|
||||
case SIGTRAP:
|
||||
switch (code) {
|
||||
case TRAP_BRKPT: return "TRAP_BRKPT";
|
||||
case TRAP_TRACE: return "TRAP_TRACE";
|
||||
case TRAP_BRANCH: return "TRAP_BRANCH";
|
||||
case TRAP_HWBKPT: return "TRAP_HWBKPT";
|
||||
}
|
||||
static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
|
||||
break;
|
||||
}
|
||||
// Then the other codes...
|
||||
switch (code) {
|
||||
case SI_USER: return "SI_USER";
|
||||
case SI_KERNEL: return "SI_KERNEL";
|
||||
case SI_QUEUE: return "SI_QUEUE";
|
||||
case SI_TIMER: return "SI_TIMER";
|
||||
case SI_MESGQ: return "SI_MESGQ";
|
||||
case SI_ASYNCIO: return "SI_ASYNCIO";
|
||||
case SI_SIGIO: return "SI_SIGIO";
|
||||
case SI_TKILL: return "SI_TKILL";
|
||||
case SI_DETHREAD: return "SI_DETHREAD";
|
||||
}
|
||||
// Then give up...
|
||||
return "?";
|
||||
}
|
||||
|
||||
static void dump_header_info(log_t* log) {
|
||||
char fingerprint[PROPERTY_VALUE_MAX];
|
||||
char revision[PROPERTY_VALUE_MAX];
|
||||
|
||||
property_get("ro.build.fingerprint", fingerprint, "unknown");
|
||||
property_get("ro.revision", revision, "unknown");
|
||||
|
||||
_LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint);
|
||||
_LOG(log, logtype::HEADER, "Revision: '%s'\n", revision);
|
||||
_LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
|
||||
}
|
||||
|
||||
static void dump_signal_info(log_t* log, pid_t tid, int signal, int si_code) {
|
||||
siginfo_t si;
|
||||
memset(&si, 0, sizeof(si));
|
||||
if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
|
||||
ALOGE("cannot get siginfo: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
// bionic has to re-raise some signals, which overwrites the si_code with SI_TKILL.
|
||||
si.si_code = si_code;
|
||||
|
||||
char addr_desc[32]; // ", fault addr 0x1234"
|
||||
if (signal_has_si_addr(signal)) {
|
||||
snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
|
||||
} else {
|
||||
snprintf(addr_desc, sizeof(addr_desc), "--------");
|
||||
}
|
||||
|
||||
_LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n",
|
||||
signal, get_signame(signal), si.si_code, get_sigcode(signal, si.si_code), addr_desc);
|
||||
}
|
||||
|
||||
static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) {
|
||||
char path[64];
|
||||
char threadnamebuf[1024];
|
||||
char* threadname = nullptr;
|
||||
FILE *fp;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/comm", tid);
|
||||
if ((fp = fopen(path, "r"))) {
|
||||
threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
|
||||
fclose(fp);
|
||||
if (threadname) {
|
||||
size_t len = strlen(threadname);
|
||||
if (len && threadname[len - 1] == '\n') {
|
||||
threadname[len - 1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
// Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
|
||||
static const char logd[] = "logd";
|
||||
if (threadname != nullptr && !strncmp(threadname, logd, sizeof(logd) - 1)
|
||||
&& (!threadname[sizeof(logd) - 1] || (threadname[sizeof(logd) - 1] == '.'))) {
|
||||
log->should_retrieve_logcat = false;
|
||||
}
|
||||
|
||||
char procnamebuf[1024];
|
||||
char* procname = nullptr;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||
if ((fp = fopen(path, "r"))) {
|
||||
procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
_LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid,
|
||||
threadname ? threadname : "UNKNOWN", procname ? procname : "UNKNOWN");
|
||||
}
|
||||
|
||||
static void dump_stack_segment(
|
||||
Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) {
|
||||
// Read the data all at once.
|
||||
word_t stack_data[words];
|
||||
size_t bytes_read = backtrace->Read(*sp, reinterpret_cast<uint8_t*>(&stack_data[0]), sizeof(word_t) * words);
|
||||
words = bytes_read / sizeof(word_t);
|
||||
std::string line;
|
||||
for (size_t i = 0; i < words; i++) {
|
||||
line = " ";
|
||||
if (i == 0 && label >= 0) {
|
||||
// Print the label once.
|
||||
line += android::base::StringPrintf("#%02d ", label);
|
||||
} else {
|
||||
line += " ";
|
||||
}
|
||||
line += android::base::StringPrintf("%" PRIPTR " %" PRIPTR, *sp, stack_data[i]);
|
||||
|
||||
backtrace_map_t map;
|
||||
backtrace->FillInMap(stack_data[i], &map);
|
||||
if (BacktraceMap::IsValid(map) && !map.name.empty()) {
|
||||
line += " " + map.name;
|
||||
uintptr_t offset = 0;
|
||||
std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset));
|
||||
if (!func_name.empty()) {
|
||||
line += " (" + func_name;
|
||||
if (offset) {
|
||||
line += android::base::StringPrintf("+%" PRIuPTR, offset);
|
||||
}
|
||||
line += ')';
|
||||
}
|
||||
}
|
||||
_LOG(log, logtype::STACK, "%s\n", line.c_str());
|
||||
|
||||
*sp += sizeof(word_t);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_stack(Backtrace* backtrace, log_t* log) {
|
||||
size_t first = 0, last;
|
||||
for (size_t i = 0; i < backtrace->NumFrames(); i++) {
|
||||
const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
|
||||
if (frame->sp) {
|
||||
if (!first) {
|
||||
first = i+1;
|
||||
}
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
first--;
|
||||
|
||||
// Dump a few words before the first frame.
|
||||
word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t);
|
||||
dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1);
|
||||
|
||||
// Dump a few words from all successive frames.
|
||||
// Only log the first 3 frames, put the rest in the tombstone.
|
||||
for (size_t i = first; i <= last; i++) {
|
||||
const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
|
||||
if (sp != frame->sp) {
|
||||
_LOG(log, logtype::STACK, " ........ ........\n");
|
||||
sp = frame->sp;
|
||||
}
|
||||
if (i == last) {
|
||||
dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i);
|
||||
if (sp < frame->sp + frame->stack_size) {
|
||||
_LOG(log, logtype::STACK, " ........ ........\n");
|
||||
}
|
||||
} else {
|
||||
size_t words = frame->stack_size / sizeof(word_t);
|
||||
if (words == 0) {
|
||||
words = 1;
|
||||
} else if (words > STACK_WORDS) {
|
||||
words = STACK_WORDS;
|
||||
}
|
||||
dump_stack_segment(backtrace, log, &sp, words, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_addr_string(uintptr_t addr) {
|
||||
std::string addr_str;
|
||||
#if defined(__LP64__)
|
||||
addr_str = android::base::StringPrintf("%08x'%08x",
|
||||
static_cast<uint32_t>(addr >> 32),
|
||||
static_cast<uint32_t>(addr & 0xffffffff));
|
||||
#else
|
||||
addr_str = android::base::StringPrintf("%08x", addr);
|
||||
#endif
|
||||
return addr_str;
|
||||
}
|
||||
|
||||
static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
|
||||
if (address == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
address += sizeof(size_t); // Skip the buffer length.
|
||||
|
||||
char msg[512];
|
||||
memset(msg, 0, sizeof(msg));
|
||||
char* p = &msg[0];
|
||||
while (p < &msg[sizeof(msg)]) {
|
||||
word_t data;
|
||||
size_t len = sizeof(word_t);
|
||||
if (!backtrace->ReadWord(address, &data)) {
|
||||
break;
|
||||
}
|
||||
address += sizeof(word_t);
|
||||
|
||||
while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
msg[sizeof(msg) - 1] = '\0';
|
||||
|
||||
_LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
|
||||
}
|
||||
|
||||
static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
|
||||
bool print_fault_address_marker = false;
|
||||
uintptr_t addr = 0;
|
||||
siginfo_t si;
|
||||
memset(&si, 0, sizeof(si));
|
||||
if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) {
|
||||
print_fault_address_marker = signal_has_si_addr(si.si_signo);
|
||||
addr = reinterpret_cast<uintptr_t>(si.si_addr);
|
||||
} else {
|
||||
ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
|
||||
}
|
||||
|
||||
_LOG(log, logtype::MAPS, "\n");
|
||||
if (!print_fault_address_marker) {
|
||||
_LOG(log, logtype::MAPS, "memory map:\n");
|
||||
} else {
|
||||
_LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
|
||||
if (map->begin() != map->end() && addr < map->begin()->start) {
|
||||
_LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
|
||||
get_addr_string(addr).c_str());
|
||||
print_fault_address_marker = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string line;
|
||||
for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
|
||||
line = " ";
|
||||
if (print_fault_address_marker) {
|
||||
if (addr < it->start) {
|
||||
_LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
|
||||
get_addr_string(addr).c_str());
|
||||
print_fault_address_marker = false;
|
||||
} else if (addr >= it->start && addr < it->end) {
|
||||
line = "--->";
|
||||
print_fault_address_marker = false;
|
||||
}
|
||||
}
|
||||
line += get_addr_string(it->start) + '-' + get_addr_string(it->end - 1) + ' ';
|
||||
if (it->flags & PROT_READ) {
|
||||
line += 'r';
|
||||
} else {
|
||||
line += '-';
|
||||
}
|
||||
if (it->flags & PROT_WRITE) {
|
||||
line += 'w';
|
||||
} else {
|
||||
line += '-';
|
||||
}
|
||||
if (it->flags & PROT_EXEC) {
|
||||
line += 'x';
|
||||
} else {
|
||||
line += '-';
|
||||
}
|
||||
line += android::base::StringPrintf(" %8" PRIxPTR " %8" PRIxPTR,
|
||||
it->offset, it->end - it->start);
|
||||
bool space_needed = true;
|
||||
if (it->name.length() > 0) {
|
||||
space_needed = false;
|
||||
line += " " + it->name;
|
||||
std::string build_id;
|
||||
if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) {
|
||||
line += " (BuildId: " + build_id + ")";
|
||||
}
|
||||
}
|
||||
if (it->load_base != 0) {
|
||||
if (space_needed) {
|
||||
line += ' ';
|
||||
}
|
||||
line += android::base::StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
|
||||
}
|
||||
_LOG(log, logtype::MAPS, "%s\n", line.c_str());
|
||||
}
|
||||
if (print_fault_address_marker) {
|
||||
_LOG(log, logtype::MAPS, "--->Fault address falls at %s after any mapped regions\n",
|
||||
get_addr_string(addr).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
|
||||
if (backtrace->NumFrames()) {
|
||||
_LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
|
||||
dump_backtrace_to_log(backtrace, log, " ");
|
||||
|
||||
_LOG(log, logtype::STACK, "\nstack:\n");
|
||||
dump_stack(backtrace, log);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_thread(log_t* log, pid_t pid, pid_t tid, BacktraceMap* map, int signal,
|
||||
int si_code, uintptr_t abort_msg_address, bool primary_thread) {
|
||||
log->current_tid = tid;
|
||||
if (!primary_thread) {
|
||||
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
|
||||
}
|
||||
dump_thread_info(log, pid, tid);
|
||||
|
||||
if (signal) {
|
||||
dump_signal_info(log, tid, signal, si_code);
|
||||
}
|
||||
|
||||
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
|
||||
if (primary_thread) {
|
||||
dump_abort_message(backtrace.get(), log, abort_msg_address);
|
||||
}
|
||||
dump_registers(log, tid);
|
||||
if (backtrace->Unwind(0)) {
|
||||
dump_backtrace_and_stack(backtrace.get(), log);
|
||||
} else {
|
||||
ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
|
||||
}
|
||||
|
||||
if (primary_thread) {
|
||||
dump_memory_and_code(log, backtrace.get());
|
||||
if (map) {
|
||||
dump_all_maps(backtrace.get(), map, log, tid);
|
||||
}
|
||||
}
|
||||
|
||||
log->current_tid = log->crashed_tid;
|
||||
}
|
||||
|
||||
// Reads the contents of the specified log device, filters out the entries
|
||||
// that don't match the specified pid, and writes them to the tombstone file.
|
||||
//
|
||||
// If "tail" is non-zero, log the last "tail" number of lines.
|
||||
static EventTagMap* g_eventTagMap = NULL;
|
||||
|
||||
static void dump_log_file(
|
||||
log_t* log, pid_t pid, const char* filename, unsigned int tail) {
|
||||
bool first = true;
|
||||
struct logger_list* logger_list;
|
||||
|
||||
if (!log->should_retrieve_logcat) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger_list = android_logger_list_open(
|
||||
android_name_to_log_id(filename), ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tail, pid);
|
||||
|
||||
if (!logger_list) {
|
||||
ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
struct log_msg log_entry;
|
||||
|
||||
while (true) {
|
||||
ssize_t actual = android_logger_list_read(logger_list, &log_entry);
|
||||
struct logger_entry* entry;
|
||||
|
||||
if (actual < 0) {
|
||||
if (actual == -EINTR) {
|
||||
// interrupted by signal, retry
|
||||
continue;
|
||||
} else if (actual == -EAGAIN) {
|
||||
// non-blocking EOF; we're done
|
||||
break;
|
||||
} else {
|
||||
ALOGE("Error while reading log: %s\n", strerror(-actual));
|
||||
break;
|
||||
}
|
||||
} else if (actual == 0) {
|
||||
ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: if you ALOGV something here, this will spin forever,
|
||||
// because you will be writing as fast as you're reading. Any
|
||||
// high-frequency debug diagnostics should just be written to
|
||||
// the tombstone file.
|
||||
|
||||
entry = &log_entry.entry_v1;
|
||||
|
||||
if (first) {
|
||||
_LOG(log, logtype::LOGS, "--------- %slog %s\n",
|
||||
tail ? "tail end of " : "", filename);
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Msg format is: <priority:1><tag:N>\0<message:N>\0
|
||||
//
|
||||
// We want to display it in the same format as "logcat -v threadtime"
|
||||
// (although in this case the pid is redundant).
|
||||
static const char* kPrioChars = "!.VDIWEFS";
|
||||
unsigned hdr_size = log_entry.entry.hdr_size;
|
||||
if (!hdr_size) {
|
||||
hdr_size = sizeof(log_entry.entry_v1);
|
||||
}
|
||||
char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size;
|
||||
|
||||
char timeBuf[32];
|
||||
time_t sec = static_cast<time_t>(entry->sec);
|
||||
struct tm tmBuf;
|
||||
struct tm* ptm;
|
||||
ptm = localtime_r(&sec, &tmBuf);
|
||||
strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
|
||||
|
||||
if (log_entry.id() == LOG_ID_EVENTS) {
|
||||
if (!g_eventTagMap) {
|
||||
g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
|
||||
}
|
||||
AndroidLogEntry e;
|
||||
char buf[512];
|
||||
android_log_processBinaryLogBuffer(entry, &e, g_eventTagMap, buf, sizeof(buf));
|
||||
_LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
|
||||
timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
|
||||
'I', e.tag, e.message);
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned char prio = msg[0];
|
||||
char* tag = msg + 1;
|
||||
msg = tag + strlen(tag) + 1;
|
||||
|
||||
// consume any trailing newlines
|
||||
char* nl = msg + strlen(msg) - 1;
|
||||
while (nl >= msg && *nl == '\n') {
|
||||
*nl-- = '\0';
|
||||
}
|
||||
|
||||
char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
|
||||
|
||||
// Look for line breaks ('\n') and display each text line
|
||||
// on a separate line, prefixed with the header, like logcat does.
|
||||
do {
|
||||
nl = strchr(msg, '\n');
|
||||
if (nl) {
|
||||
*nl = '\0';
|
||||
++nl;
|
||||
}
|
||||
|
||||
_LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
|
||||
timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
|
||||
prioChar, tag, msg);
|
||||
} while ((msg = nl));
|
||||
}
|
||||
|
||||
android_logger_list_free(logger_list);
|
||||
}
|
||||
|
||||
// Dumps the logs generated by the specified pid to the tombstone, from both
|
||||
// "system" and "main" log devices. Ideally we'd interleave the output.
|
||||
static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
|
||||
dump_log_file(log, pid, "system", tail);
|
||||
dump_log_file(log, pid, "main", tail);
|
||||
}
|
||||
|
||||
// Dumps all information about the specified pid to the tombstone.
|
||||
static void dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid,
|
||||
const std::set<pid_t>& siblings, int signal, int si_code,
|
||||
uintptr_t abort_msg_address) {
|
||||
// don't copy log messages to tombstone unless this is a dev device
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.debuggable", value, "0");
|
||||
bool want_logs = (value[0] == '1');
|
||||
|
||||
_LOG(log, logtype::HEADER,
|
||||
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
|
||||
dump_header_info(log);
|
||||
dump_thread(log, pid, tid, map, signal, si_code, abort_msg_address, true);
|
||||
if (want_logs) {
|
||||
dump_logs(log, pid, 5);
|
||||
}
|
||||
|
||||
if (!siblings.empty()) {
|
||||
for (pid_t sibling : siblings) {
|
||||
dump_thread(log, pid, sibling, map, 0, 0, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (want_logs) {
|
||||
dump_logs(log, pid, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// open_tombstone - find an available tombstone slot, if any, of the
|
||||
// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
|
||||
// file is available, we reuse the least-recently-modified file.
|
||||
int open_tombstone(std::string* out_path) {
|
||||
// In a single pass, find an available slot and, in case none
|
||||
// exist, find and record the least-recently-modified file.
|
||||
char path[128];
|
||||
int fd = -1;
|
||||
int oldest = -1;
|
||||
struct stat oldest_sb;
|
||||
for (int i = 0; i < MAX_TOMBSTONES; i++) {
|
||||
snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
|
||||
|
||||
struct stat sb;
|
||||
if (stat(path, &sb) == 0) {
|
||||
if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
|
||||
oldest = i;
|
||||
oldest_sb.st_mtime = sb.st_mtime;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (errno != ENOENT) continue;
|
||||
|
||||
fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
|
||||
if (fd < 0) continue; // raced ?
|
||||
|
||||
if (out_path) {
|
||||
*out_path = path;
|
||||
}
|
||||
fchown(fd, AID_SYSTEM, AID_SYSTEM);
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (oldest < 0) {
|
||||
ALOGE("debuggerd: failed to find a valid tombstone, default to using tombstone 0.\n");
|
||||
oldest = 0;
|
||||
}
|
||||
|
||||
// we didn't find an available file, so we clobber the oldest one
|
||||
snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
|
||||
fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
|
||||
if (fd < 0) {
|
||||
ALOGE("debuggerd: failed to open tombstone file '%s': %s\n", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (out_path) {
|
||||
*out_path = path;
|
||||
}
|
||||
fchown(fd, AID_SYSTEM, AID_SYSTEM);
|
||||
return fd;
|
||||
}
|
||||
|
||||
void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid,
|
||||
const std::set<pid_t>& siblings, int signal, int original_si_code,
|
||||
uintptr_t abort_msg_address, std::string* amfd_data) {
|
||||
log_t log;
|
||||
log.current_tid = tid;
|
||||
log.crashed_tid = tid;
|
||||
|
||||
if (tombstone_fd < 0) {
|
||||
ALOGE("debuggerd: skipping tombstone write, nothing to do.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log.tfd = tombstone_fd;
|
||||
log.amfd_data = amfd_data;
|
||||
dump_crash(&log, map, pid, tid, siblings, signal, original_si_code, abort_msg_address);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 _DEBUGGERD_TOMBSTONE_H
|
||||
#define _DEBUGGERD_TOMBSTONE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
class BacktraceMap;
|
||||
|
||||
/* Create and open a tombstone file for writing.
|
||||
* Returns a writable file descriptor, or -1 with errno set appropriately.
|
||||
* If out_path is non-null, *out_path is set to the path of the tombstone file.
|
||||
*/
|
||||
int open_tombstone(std::string* path);
|
||||
|
||||
/* Creates a tombstone file and writes the crash dump to it. */
|
||||
void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid,
|
||||
const std::set<pid_t>& siblings, int signal, int original_si_code,
|
||||
uintptr_t abort_msg_address, std::string* amfd_data);
|
||||
|
||||
#endif // _DEBUGGERD_TOMBSTONE_H
|
||||
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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 "DEBUG"
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <log/log.h>
|
||||
|
||||
constexpr int SLEEP_TIME_USEC = 50000; // 0.05 seconds
|
||||
constexpr int MAX_TOTAL_SLEEP_USEC = 10000000; // 10 seconds
|
||||
|
||||
// Whitelist output desired in the logcat output.
|
||||
bool is_allowed_in_logcat(enum logtype ltype) {
|
||||
if ((ltype == HEADER)
|
||||
|| (ltype == REGISTERS)
|
||||
|| (ltype == BACKTRACE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
|
||||
bool write_to_tombstone = (log->tfd != -1);
|
||||
bool write_to_logcat = is_allowed_in_logcat(ltype)
|
||||
&& log->crashed_tid != -1
|
||||
&& log->current_tid != -1
|
||||
&& (log->crashed_tid == log->current_tid);
|
||||
|
||||
char buf[512];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
size_t len = strlen(buf);
|
||||
if (len <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (write_to_tombstone) {
|
||||
TEMP_FAILURE_RETRY(write(log->tfd, buf, len));
|
||||
}
|
||||
|
||||
if (write_to_logcat) {
|
||||
__android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, buf);
|
||||
if (log->amfd_data != nullptr) {
|
||||
*log->amfd_data += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int wait_for_signal(pid_t tid, int* total_sleep_time_usec) {
|
||||
while (true) {
|
||||
int status;
|
||||
pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
|
||||
if (n == -1) {
|
||||
ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
|
||||
return -1;
|
||||
} else if (n == tid) {
|
||||
if (WIFSTOPPED(status)) {
|
||||
return WSTOPSIG(status);
|
||||
} else {
|
||||
ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
|
||||
// This is the only circumstance under which we can allow a detach
|
||||
// to fail with ESRCH, which indicates the tid has exited.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*total_sleep_time_usec > MAX_TOTAL_SLEEP_USEC) {
|
||||
ALOGE("timed out waiting for stop signal: tid=%d", tid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
usleep(SLEEP_TIME_USEC);
|
||||
*total_sleep_time_usec += SLEEP_TIME_USEC;
|
||||
}
|
||||
}
|
||||
|
||||
#define MEMORY_BYTES_TO_DUMP 256
|
||||
#define MEMORY_BYTES_PER_LINE 16
|
||||
|
||||
void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) {
|
||||
std::string log_msg;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
android::base::StringAppendV(&log_msg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
// Align the address to sizeof(long) and start 32 bytes before the address.
|
||||
addr &= ~(sizeof(long) - 1);
|
||||
if (addr >= 4128) {
|
||||
addr -= 32;
|
||||
}
|
||||
|
||||
// Don't bother if the address looks too low, or looks too high.
|
||||
if (addr < 4096 ||
|
||||
#if defined(__LP64__)
|
||||
addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) {
|
||||
#else
|
||||
addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) {
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str());
|
||||
|
||||
// Dump 256 bytes
|
||||
uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
|
||||
memset(data, 0, MEMORY_BYTES_TO_DUMP);
|
||||
size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
|
||||
if (bytes % sizeof(uintptr_t) != 0) {
|
||||
// This should never happen, but just in case.
|
||||
ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
|
||||
bytes &= ~(sizeof(uintptr_t) - 1);
|
||||
}
|
||||
|
||||
uintptr_t start = 0;
|
||||
bool skip_2nd_read = false;
|
||||
if (bytes == 0) {
|
||||
// In this case, we might want to try another read at the beginning of
|
||||
// the next page only if it's within the amount of memory we would have
|
||||
// read.
|
||||
size_t page_size = sysconf(_SC_PAGE_SIZE);
|
||||
start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
|
||||
if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
|
||||
skip_2nd_read = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
|
||||
// Try to do one more read. This could happen if a read crosses a map,
|
||||
// but the maps do not have any break between them. Or it could happen
|
||||
// if reading from an unreadable map, but the read would cross back
|
||||
// into a readable map. Only requires one extra read because a map has
|
||||
// to contain at least one page, and the total number of bytes to dump
|
||||
// is smaller than a page.
|
||||
size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
|
||||
sizeof(data) - bytes - start);
|
||||
bytes += bytes2;
|
||||
if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
|
||||
// This should never happen, but we'll try and continue any way.
|
||||
ALOGE("Bytes after second read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
|
||||
bytes &= ~(sizeof(uintptr_t) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Dump the code around memory as:
|
||||
// addr contents ascii
|
||||
// 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q
|
||||
// 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p......
|
||||
// On 32-bit machines, there are still 16 bytes per line but addresses and
|
||||
// words are of course presented differently.
|
||||
uintptr_t* data_ptr = data;
|
||||
size_t current = 0;
|
||||
size_t total_bytes = start + bytes;
|
||||
for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
|
||||
std::string logline;
|
||||
android::base::StringAppendF(&logline, " %" PRIPTR, addr);
|
||||
|
||||
addr += MEMORY_BYTES_PER_LINE;
|
||||
std::string ascii;
|
||||
for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
|
||||
if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
|
||||
android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
|
||||
|
||||
// Fill out the ascii string from the data.
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);
|
||||
for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) {
|
||||
if (*ptr >= 0x20 && *ptr < 0x7f) {
|
||||
ascii += *ptr;
|
||||
} else {
|
||||
ascii += '.';
|
||||
}
|
||||
}
|
||||
data_ptr++;
|
||||
} else {
|
||||
logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
|
||||
ascii += std::string(sizeof(uintptr_t), '.');
|
||||
}
|
||||
current += sizeof(uintptr_t);
|
||||
}
|
||||
_LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str());
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/* system/debuggerd/utility.h
|
||||
**
|
||||
** Copyright 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.
|
||||
** 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 _DEBUGGERD_UTILITY_H
|
||||
#define _DEBUGGERD_UTILITY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
// Figure out the abi based on defined macros.
|
||||
#if defined(__arm__)
|
||||
#define ABI_STRING "arm"
|
||||
#elif defined(__aarch64__)
|
||||
#define ABI_STRING "arm64"
|
||||
#elif defined(__mips__) && !defined(__LP64__)
|
||||
#define ABI_STRING "mips"
|
||||
#elif defined(__mips__) && defined(__LP64__)
|
||||
#define ABI_STRING "mips64"
|
||||
#elif defined(__i386__)
|
||||
#define ABI_STRING "x86"
|
||||
#elif defined(__x86_64__)
|
||||
#define ABI_STRING "x86_64"
|
||||
#else
|
||||
#error "Unsupported ABI"
|
||||
#endif
|
||||
|
||||
|
||||
struct log_t{
|
||||
// Tombstone file descriptor.
|
||||
int tfd;
|
||||
// Data to be sent to the Activity Manager.
|
||||
std::string* amfd_data;
|
||||
// The tid of the thread that crashed.
|
||||
pid_t crashed_tid;
|
||||
// The tid of the thread we are currently working with.
|
||||
pid_t current_tid;
|
||||
// logd daemon crash, can block asking for logcat data, allow suppression.
|
||||
bool should_retrieve_logcat;
|
||||
|
||||
log_t()
|
||||
: tfd(-1), amfd_data(nullptr), crashed_tid(-1), current_tid(-1),
|
||||
should_retrieve_logcat(true) {}
|
||||
};
|
||||
|
||||
// List of types of logs to simplify the logging decision in _LOG
|
||||
enum logtype {
|
||||
HEADER,
|
||||
THREAD,
|
||||
REGISTERS,
|
||||
FP_REGISTERS,
|
||||
BACKTRACE,
|
||||
MAPS,
|
||||
MEMORY,
|
||||
STACK,
|
||||
LOGS
|
||||
};
|
||||
|
||||
// Log information onto the tombstone.
|
||||
void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 3, 4)));
|
||||
|
||||
int wait_for_signal(pid_t tid, int* total_sleep_time_usec);
|
||||
|
||||
void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
|
||||
|
||||
#endif // _DEBUGGERD_UTILITY_H
|
||||
@@ -1,15 +0,0 @@
|
||||
.globl crash1
|
||||
.globl crashnostack
|
||||
|
||||
crash1:
|
||||
movl $0xa5a50000, %eax
|
||||
movl $0xa5a50001, %ebx
|
||||
movl $0xa5a50002, %ecx
|
||||
|
||||
movl $0, %edx
|
||||
jmp *%edx
|
||||
|
||||
|
||||
crashnostack:
|
||||
movl $0, %ebp
|
||||
jmp *%ebp
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "DEBUG"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include "machine.h"
|
||||
#include "utility.h"
|
||||
|
||||
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
|
||||
struct pt_regs r;
|
||||
if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
|
||||
ALOGE("cannot get registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.eax), "memory near eax:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.ebx), "memory near ebx:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.ecx), "memory near ecx:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.edx), "memory near edx:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.esi), "memory near esi:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.edi), "memory near edi:");
|
||||
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.eip), "code around eip:");
|
||||
}
|
||||
|
||||
void dump_registers(log_t* log, pid_t tid) {
|
||||
struct pt_regs r;
|
||||
if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
|
||||
ALOGE("cannot get registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG(log, logtype::REGISTERS, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n",
|
||||
r.eax, r.ebx, r.ecx, r.edx);
|
||||
_LOG(log, logtype::REGISTERS, " esi %08lx edi %08lx\n",
|
||||
r.esi, r.edi);
|
||||
_LOG(log, logtype::REGISTERS, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n",
|
||||
r.xcs, r.xds, r.xes, r.xfs, r.xss);
|
||||
_LOG(log, logtype::REGISTERS, " eip %08lx ebp %08lx esp %08lx flags %08lx\n",
|
||||
r.eip, r.ebp, r.esp, r.eflags);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.globl crash1
|
||||
.globl crashnostack
|
||||
|
||||
crash1:
|
||||
movl $0xa5a50000, %eax
|
||||
movl $0xa5a50001, %ebx
|
||||
movl $0xa5a50002, %ecx
|
||||
|
||||
movl $0, %edx
|
||||
jmp *%rdx
|
||||
|
||||
|
||||
crashnostack:
|
||||
movl $0, %ebp
|
||||
jmp *%rbp
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
** Copyright 2013, 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 "DEBUG"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <string.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include "machine.h"
|
||||
#include "utility.h"
|
||||
|
||||
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
|
||||
struct user_regs_struct r;
|
||||
if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
|
||||
ALOGE("cannot get registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:");
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:");
|
||||
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:");
|
||||
}
|
||||
|
||||
void dump_registers(log_t* log, pid_t tid) {
|
||||
struct user_regs_struct r;
|
||||
if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
|
||||
ALOGE("cannot get registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n",
|
||||
r.rax, r.rbx, r.rcx, r.rdx);
|
||||
_LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n",
|
||||
r.rsi, r.rdi);
|
||||
_LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n",
|
||||
r.r8, r.r9, r.r10, r.r11);
|
||||
_LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n",
|
||||
r.r12, r.r13, r.r14, r.r15);
|
||||
_LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n",
|
||||
r.cs, r.ss);
|
||||
_LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n",
|
||||
r.rip, r.rbp, r.rsp, r.eflags);
|
||||
}
|
||||
@@ -1,95 +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 $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := $(module)
|
||||
LOCAL_MODULE_TAGS := $(module_tag)
|
||||
LOCAL_MULTILIB := $($(module)_multilib)
|
||||
ifeq ($(LOCAL_MULTILIB),both)
|
||||
ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
|
||||
LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
|
||||
LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
|
||||
endif
|
||||
endif
|
||||
|
||||
# ifeq ($(build_type),target)
|
||||
# include $(LLVM_DEVICE_BUILD_MK)
|
||||
# else
|
||||
# include $(LLVM_HOST_BUILD_MK)
|
||||
# endif
|
||||
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES += \
|
||||
$(LOCAL_PATH)/Android.mk \
|
||||
$(LOCAL_PATH)/Android.build.mk \
|
||||
|
||||
LOCAL_CFLAGS += \
|
||||
$(libbacktrace_common_cflags) \
|
||||
$($(module)_cflags) \
|
||||
$($(module)_cflags_$(build_type)) \
|
||||
|
||||
LOCAL_CLANG_CFLAGS += \
|
||||
$(libbacktrace_common_clang_cflags) \
|
||||
|
||||
LOCAL_CONLYFLAGS += \
|
||||
$(libbacktrace_common_conlyflags) \
|
||||
$($(module)_conlyflags) \
|
||||
$($(module)_conlyflags_$(build_type)) \
|
||||
|
||||
LOCAL_CPPFLAGS += \
|
||||
$(libbacktrace_common_cppflags) \
|
||||
$($(module)_cppflags) \
|
||||
$($(module)_cppflags_$(build_type)) \
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(libbacktrace_common_c_includes) \
|
||||
$($(module)_c_includes) \
|
||||
$($(module)_c_includes_$(build_type)) \
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$($(module)_src_files) \
|
||||
$($(module)_src_files_$(build_type)) \
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += \
|
||||
$($(module)_static_libraries) \
|
||||
$($(module)_static_libraries_$(build_type)) \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES += \
|
||||
$($(module)_shared_libraries) \
|
||||
$($(module)_shared_libraries_$(build_type)) \
|
||||
|
||||
LOCAL_LDLIBS += \
|
||||
$($(module)_ldlibs) \
|
||||
$($(module)_ldlibs_$(build_type)) \
|
||||
|
||||
LOCAL_STRIP_MODULE := $($(module)_strip_module)
|
||||
|
||||
ifeq ($(build_type),target)
|
||||
include $(BUILD_$(build_target))
|
||||
endif
|
||||
|
||||
ifeq ($(build_type),host)
|
||||
# Only build if host builds are supported.
|
||||
ifeq ($(build_host),true)
|
||||
# -fno-omit-frame-pointer should be set for host build. Because currently
|
||||
# libunwind can't recognize .debug_frame using dwarf version 4, and it relies
|
||||
# on stack frame pointer to do unwinding on x86.
|
||||
# $(LLVM_HOST_BUILD_MK) overwrites -fno-omit-frame-pointer. so the below line
|
||||
# must be after the include.
|
||||
LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
|
||||
include $(BUILD_HOST_$(build_target))
|
||||
endif
|
||||
endif
|
||||
@@ -1,124 +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.
|
||||
#
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
libbacktrace_common_cflags := \
|
||||
-Wall \
|
||||
-Werror
|
||||
|
||||
libbacktrace_common_conlyflags := \
|
||||
-std=gnu99 \
|
||||
|
||||
libbacktrace_common_cppflags := \
|
||||
-std=gnu++11 \
|
||||
-I$(HERE_PATH)/crash_dump/libbase/include \
|
||||
-I$(HERE_PATH)/crash_dump/libunwind/include/tdep
|
||||
|
||||
# The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
|
||||
libbacktrace_common_clang_cflags += \
|
||||
-Wno-inline-asm
|
||||
|
||||
build_host := false
|
||||
ifeq ($(HOST_OS),linux)
|
||||
ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
|
||||
build_host := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# LLVM_ROOT_PATH := external/llvm
|
||||
# include $(LLVM_ROOT_PATH)/llvm.mk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The libbacktrace library.
|
||||
#-------------------------------------------------------------------------
|
||||
libbacktrace_src_files := \
|
||||
Backtrace.cpp \
|
||||
BacktraceCurrent.cpp \
|
||||
BacktraceMap.cpp \
|
||||
BacktracePtrace.cpp \
|
||||
thread_utils.c \
|
||||
ThreadEntry.cpp \
|
||||
UnwindCurrent.cpp \
|
||||
UnwindMap.cpp \
|
||||
UnwindPtrace.cpp \
|
||||
|
||||
libbacktrace_shared_ldlibs := -llog
|
||||
libbacktrace_shared_libraries := libunwind libcrashdumpbase
|
||||
|
||||
module := libbacktrace
|
||||
module_tag := optional
|
||||
build_type := target
|
||||
build_target := SHARED_LIBRARY
|
||||
include $(LOCAL_PATH)/Android.build.mk
|
||||
build_type := host
|
||||
libbacktrace_multilib := both
|
||||
include $(LOCAL_PATH)/Android.build.mk
|
||||
libbacktrace_static_ldlibs := -llog -lunwind
|
||||
# -lbase
|
||||
|
||||
# libbacktrace_static_libraries := libunwind
|
||||
|
||||
|
||||
build_target := STATIC_LIBRARY
|
||||
include $(LOCAL_PATH)/Android.build.mk
|
||||
libbacktrace_static_libraries :=
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The libbacktrace_offline shared library.
|
||||
#-------------------------------------------------------------------------
|
||||
libbacktrace_offline_src_files := \
|
||||
BacktraceOffline.cpp \
|
||||
|
||||
# Use shared llvm library on device to save space.
|
||||
libbacktrace_offline_shared_libraries_target := \
|
||||
libbacktrace \
|
||||
libbase \
|
||||
liblog \
|
||||
libunwind \
|
||||
libutils \
|
||||
libLLVM \
|
||||
|
||||
libbacktrace_offline_static_libraries_target := \
|
||||
libziparchive \
|
||||
libz \
|
||||
|
||||
# Use static llvm libraries on host to remove dependency on 32-bit llvm shared library
|
||||
# which is not included in the prebuilt.
|
||||
libbacktrace_offline_static_libraries_host := \
|
||||
libbacktrace \
|
||||
libunwind \
|
||||
libziparchive-host \
|
||||
libz \
|
||||
libbase \
|
||||
liblog \
|
||||
libutils \
|
||||
libLLVMObject \
|
||||
libLLVMBitReader \
|
||||
libLLVMMC \
|
||||
libLLVMMCParser \
|
||||
libLLVMCore \
|
||||
libLLVMSupport \
|
||||
|
||||
module := libbacktrace_offline
|
||||
build_type := target
|
||||
build_target := STATIC_LIBRARY
|
||||
libbacktrace_offline_multilib := both
|
||||
# i don't know if this lib is required or not
|
||||
# include $(LOCAL_PATH)/Android.build.mk
|
||||
build_type := host
|
||||
# include $(LOCAL_PATH)/Android.build.mk
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "thread_utils.h"
|
||||
#include "UnwindCurrent.h"
|
||||
#include "UnwindPtrace.h"
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Backtrace functions.
|
||||
//-------------------------------------------------------------------------
|
||||
Backtrace::Backtrace(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: pid_(pid), tid_(tid), map_(map), map_shared_(true) {
|
||||
if (map_ == nullptr) {
|
||||
map_ = BacktraceMap::Create(pid);
|
||||
map_shared_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
Backtrace::~Backtrace() {
|
||||
if (map_ && !map_shared_) {
|
||||
delete map_;
|
||||
map_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
|
||||
std::string func_name = GetFunctionNameRaw(pc, offset);
|
||||
return func_name;
|
||||
}
|
||||
|
||||
bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
|
||||
if (ptr & (sizeof(word_t)-1)) {
|
||||
BACK_LOGW("invalid pointer %p", reinterpret_cast<void*>(ptr));
|
||||
*out_value = static_cast<word_t>(-1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Backtrace::FormatFrameData(size_t frame_num) {
|
||||
if (frame_num >= frames_.size()) {
|
||||
return "";
|
||||
}
|
||||
return FormatFrameData(&frames_[frame_num]);
|
||||
}
|
||||
|
||||
std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
|
||||
uintptr_t relative_pc;
|
||||
std::string map_name;
|
||||
if (BacktraceMap::IsValid(frame->map)) {
|
||||
relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
|
||||
if (!frame->map.name.empty()) {
|
||||
map_name = frame->map.name.c_str();
|
||||
if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
|
||||
map_name.resize(map_name.size() - 1);
|
||||
map_name += StringPrintf(":%" PRIPTR "]", frame->map.start);
|
||||
}
|
||||
} else {
|
||||
map_name = StringPrintf("<anonymous:%" PRIPTR ">", frame->map.start);
|
||||
}
|
||||
} else {
|
||||
map_name = "<unknown>";
|
||||
relative_pc = frame->pc;
|
||||
}
|
||||
|
||||
std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, relative_pc));
|
||||
line += map_name;
|
||||
// Special handling for non-zero offset maps, we need to print that
|
||||
// information.
|
||||
if (frame->map.offset != 0) {
|
||||
line += " (offset " + StringPrintf("0x%" PRIxPTR, frame->map.offset) + ")";
|
||||
}
|
||||
if (!frame->func_name.empty()) {
|
||||
line += " (" + frame->func_name;
|
||||
if (frame->func_offset) {
|
||||
line += StringPrintf("+%" PRIuPTR, frame->func_offset);
|
||||
}
|
||||
line += ')';
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) {
|
||||
if (map_ != nullptr) {
|
||||
map_->FillIn(pc, map);
|
||||
}
|
||||
}
|
||||
|
||||
Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
|
||||
if (pid == BACKTRACE_CURRENT_PROCESS) {
|
||||
pid = getpid();
|
||||
if (tid == BACKTRACE_CURRENT_THREAD) {
|
||||
tid = gettid();
|
||||
}
|
||||
} else if (tid == BACKTRACE_CURRENT_THREAD) {
|
||||
tid = pid;
|
||||
}
|
||||
|
||||
if (pid == getpid()) {
|
||||
return new UnwindCurrent(pid, tid, map);
|
||||
} else {
|
||||
return new UnwindPtrace(pid, tid, map);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
|
||||
switch (error) {
|
||||
case BACKTRACE_UNWIND_NO_ERROR:
|
||||
return "No error";
|
||||
case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
|
||||
return "Setup failed";
|
||||
case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
|
||||
return "No map found";
|
||||
case BACKTRACE_UNWIND_ERROR_INTERNAL:
|
||||
return "Internal libbacktrace error, please submit a bugreport";
|
||||
case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
|
||||
return "Thread doesn't exist";
|
||||
case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
|
||||
return "Thread has not repsonded to signal in time";
|
||||
case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
|
||||
return "Attempt to use an unsupported feature";
|
||||
case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
|
||||
return "Attempt to do an offline unwind without a context";
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 _GNU_SOURCE 1
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include "BacktraceCurrent.h"
|
||||
#include "BacktraceLog.h"
|
||||
#include "ThreadEntry.h"
|
||||
#include "thread_utils.h"
|
||||
|
||||
bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) {
|
||||
if (!VerifyReadWordArgs(ptr, out_value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
backtrace_map_t map;
|
||||
FillInMap(ptr, &map);
|
||||
if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) {
|
||||
*out_value = *reinterpret_cast<word_t*>(ptr);
|
||||
return true;
|
||||
} else {
|
||||
BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
|
||||
*out_value = static_cast<word_t>(-1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t BacktraceCurrent::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
|
||||
backtrace_map_t map;
|
||||
FillInMap(addr, &map);
|
||||
if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
|
||||
return 0;
|
||||
}
|
||||
bytes = MIN(map.end - addr, bytes);
|
||||
memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
|
||||
if (GetMap() == nullptr) {
|
||||
// Without a map object, we can't do anything.
|
||||
error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
|
||||
return false;
|
||||
}
|
||||
|
||||
error_ = BACKTRACE_UNWIND_NO_ERROR;
|
||||
if (ucontext) {
|
||||
return UnwindFromContext(num_ignore_frames, ucontext);
|
||||
}
|
||||
|
||||
if (Tid() != gettid()) {
|
||||
return UnwindThread(num_ignore_frames);
|
||||
}
|
||||
|
||||
return UnwindFromContext(num_ignore_frames, nullptr);
|
||||
}
|
||||
|
||||
bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) {
|
||||
if (BacktraceMap::IsValid(frame.map)) {
|
||||
const std::string library = basename(frame.map.name.c_str());
|
||||
if (library == "libunwind.so" || library == "libbacktrace.so") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void SignalLogOnly(int, siginfo_t*, void*) {
|
||||
BACK_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(), THREAD_SIGNAL);
|
||||
}
|
||||
|
||||
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
|
||||
ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
|
||||
if (!entry) {
|
||||
BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
|
||||
return;
|
||||
}
|
||||
|
||||
entry->CopyUcontextFromSigcontext(sigcontext);
|
||||
|
||||
// Indicate the ucontext is now valid.
|
||||
entry->Wake();
|
||||
|
||||
// Pause the thread until the unwind is complete. This avoids having
|
||||
// the thread run ahead causing problems.
|
||||
// The number indicates that we are waiting for the second Wake() call
|
||||
// overall which is made by the thread requesting an unwind.
|
||||
if (entry->Wait(2)) {
|
||||
// Do not remove the entry here because that can result in a deadlock
|
||||
// if the code cannot properly send a signal to the thread under test.
|
||||
entry->Wake();
|
||||
} else {
|
||||
// At this point, it is possible that entry has been freed, so just exit.
|
||||
BACK_LOGE("Timed out waiting for unwind thread to indicate it completed.");
|
||||
}
|
||||
}
|
||||
|
||||
bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) {
|
||||
// Prevent multiple threads trying to set the trigger action on different
|
||||
// threads at the same time.
|
||||
pthread_mutex_lock(&g_sigaction_mutex);
|
||||
|
||||
ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
|
||||
entry->Lock();
|
||||
|
||||
struct sigaction act, oldact;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = SignalHandler;
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&act.sa_mask);
|
||||
if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
|
||||
BACK_LOGE("sigaction failed: %s", strerror(errno));
|
||||
ThreadEntry::Remove(entry);
|
||||
pthread_mutex_unlock(&g_sigaction_mutex);
|
||||
error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
|
||||
// Do not emit an error message, this might be expected. Set the
|
||||
// error and let the caller decide.
|
||||
if (errno == ESRCH) {
|
||||
error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
|
||||
} else {
|
||||
error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
sigaction(THREAD_SIGNAL, &oldact, nullptr);
|
||||
ThreadEntry::Remove(entry);
|
||||
pthread_mutex_unlock(&g_sigaction_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for the thread to get the ucontext. The number indicates
|
||||
// that we are waiting for the first Wake() call made by the thread.
|
||||
bool wait_completed = entry->Wait(1);
|
||||
|
||||
if (!wait_completed && oldact.sa_sigaction == nullptr) {
|
||||
// If the wait failed, it could be that the signal could not be delivered
|
||||
// within the timeout. Add a signal handler that's simply going to log
|
||||
// something so that we don't crash if the signal eventually gets
|
||||
// delivered. Only do this if there isn't already an action set up.
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = SignalLogOnly;
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&act.sa_mask);
|
||||
sigaction(THREAD_SIGNAL, &act, nullptr);
|
||||
} else {
|
||||
sigaction(THREAD_SIGNAL, &oldact, nullptr);
|
||||
}
|
||||
// After the thread has received the signal, allow other unwinders to
|
||||
// continue.
|
||||
pthread_mutex_unlock(&g_sigaction_mutex);
|
||||
|
||||
bool unwind_done = false;
|
||||
if (wait_completed) {
|
||||
unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
|
||||
|
||||
// Tell the signal handler to exit and release the entry.
|
||||
entry->Wake();
|
||||
|
||||
// Wait for the thread to indicate it is done with the ThreadEntry.
|
||||
if (!entry->Wait(3)) {
|
||||
// Send a warning, but do not mark as a failure to unwind.
|
||||
BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
|
||||
}
|
||||
} else {
|
||||
// Check to see if the thread has disappeared.
|
||||
if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
|
||||
error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
|
||||
} else {
|
||||
error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
|
||||
BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
|
||||
}
|
||||
}
|
||||
|
||||
ThreadEntry::Remove(entry);
|
||||
|
||||
return unwind_done;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 _LIBBACKTRACE_BACKTRACE_CURRENT_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_CURRENT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
// The signal used to cause a thread to dump the stack.
|
||||
#if defined(__GLIBC__)
|
||||
// In order to run the backtrace_tests on the host, we can't use
|
||||
// the internal real time signals used by GLIBC. To avoid this,
|
||||
// use SIGRTMIN for the signal to dump the stack.
|
||||
#define THREAD_SIGNAL SIGRTMIN
|
||||
#else
|
||||
#define THREAD_SIGNAL (__SIGRTMIN+1)
|
||||
#endif
|
||||
|
||||
class BacktraceMap;
|
||||
|
||||
class BacktraceCurrent : public Backtrace {
|
||||
public:
|
||||
BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
|
||||
virtual ~BacktraceCurrent() {}
|
||||
|
||||
size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
|
||||
|
||||
bool ReadWord(uintptr_t ptr, word_t* out_value) override;
|
||||
|
||||
bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
|
||||
|
||||
protected:
|
||||
bool DiscardFrame(const backtrace_frame_data_t& frame);
|
||||
|
||||
private:
|
||||
bool UnwindThread(size_t num_ignore_frames);
|
||||
|
||||
virtual bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H
|
||||
@@ -1,31 +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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_LOG_H
|
||||
|
||||
#define LOG_TAG "libbacktrace"
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
// Macro to log the function name along with the warning message.
|
||||
#define BACK_LOGW(format, ...) \
|
||||
ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
#define BACK_LOGE(format, ...) \
|
||||
ALOGE("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_LOG_H
|
||||
@@ -1,153 +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 <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <backtrace/backtrace_constants.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include "thread_utils.h"
|
||||
|
||||
BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
|
||||
if (pid_ < 0) {
|
||||
pid_ = getpid();
|
||||
}
|
||||
}
|
||||
|
||||
BacktraceMap::~BacktraceMap() {
|
||||
}
|
||||
|
||||
void BacktraceMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
|
||||
for (BacktraceMap::const_iterator it = begin();
|
||||
it != end(); ++it) {
|
||||
if (addr >= it->start && addr < it->end) {
|
||||
*map = *it;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*map = {};
|
||||
}
|
||||
|
||||
bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
|
||||
unsigned long int start;
|
||||
unsigned long int end;
|
||||
char permissions[5];
|
||||
int name_pos;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// Mac OS vmmap(1) output:
|
||||
// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
|
||||
&start, &end, permissions, &name_pos) != 3) {
|
||||
#else
|
||||
// Linux /proc/<pid>/maps lines:
|
||||
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d %n",
|
||||
&start, &end, permissions, &name_pos) != 3) {
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
map->start = start;
|
||||
map->end = end;
|
||||
map->flags = PROT_NONE;
|
||||
if (permissions[0] == 'r') {
|
||||
map->flags |= PROT_READ;
|
||||
}
|
||||
if (permissions[1] == 'w') {
|
||||
map->flags |= PROT_WRITE;
|
||||
}
|
||||
if (permissions[2] == 'x') {
|
||||
map->flags |= PROT_EXEC;
|
||||
}
|
||||
|
||||
map->name = line+name_pos;
|
||||
if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
|
||||
map->name.erase(map->name.length()-1);
|
||||
}
|
||||
|
||||
ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
|
||||
reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end),
|
||||
map->flags, map->name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BacktraceMap::Build() {
|
||||
#if defined(__APPLE__)
|
||||
char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
|
||||
#else
|
||||
char path[sizeof(pid_t)*3 + sizeof("/proc//maps") + 1];
|
||||
#endif
|
||||
char line[1024];
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// cmd is guaranteed to always be big enough to hold this string.
|
||||
snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
|
||||
FILE* fp = popen(cmd, "r");
|
||||
#else
|
||||
// path is guaranteed to always be big enough to hold this string.
|
||||
snprintf(path, sizeof(path), "/proc/%d/maps", pid_);
|
||||
FILE* fp = fopen(path, "r");
|
||||
#endif
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(fgets(line, sizeof(line), fp)) {
|
||||
backtrace_map_t map;
|
||||
if (ParseLine(line, &map)) {
|
||||
maps_.push_back(map);
|
||||
}
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
pclose(fp);
|
||||
#else
|
||||
fclose(fp);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// Corkscrew and libunwind don't compile on the mac, so create a generic
|
||||
// map object.
|
||||
BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
|
||||
BacktraceMap* map = new BacktraceMap(pid);
|
||||
if (!map->Build()) {
|
||||
delete map;
|
||||
return nullptr;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
#endif
|
||||
|
||||
BacktraceMap* BacktraceMap::Create(pid_t pid, const std::vector<backtrace_map_t>& maps) {
|
||||
BacktraceMap* backtrace_map = new BacktraceMap(pid);
|
||||
backtrace_map->maps_.insert(backtrace_map->maps_.begin(), maps.begin(), maps.end());
|
||||
std::sort(backtrace_map->maps_.begin(), backtrace_map->maps_.end(),
|
||||
[](const backtrace_map_t& map1, const backtrace_map_t& map2) {
|
||||
return map1.start < map2.start;
|
||||
});
|
||||
return backtrace_map;
|
||||
}
|
||||
@@ -1,760 +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 "BacktraceOffline.h"
|
||||
|
||||
extern "C" {
|
||||
#define UNW_REMOTE_ONLY
|
||||
#include <dwarf.h>
|
||||
}
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Object/Binary.h>
|
||||
#include <llvm/Object/ELFObjectFile.h>
|
||||
#include <llvm/Object/ObjectFile.h>
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
|
||||
void Space::Clear() {
|
||||
start = 0;
|
||||
end = 0;
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
size_t Space::Read(uint64_t addr, uint8_t* buffer, size_t size) {
|
||||
if (addr >= start && addr < end) {
|
||||
size_t read_size = std::min(size, static_cast<size_t>(end - addr));
|
||||
memcpy(buffer, data + (addr - start), read_size);
|
||||
return read_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int FindProcInfo(unw_addr_space_t addr_space, unw_word_t ip, unw_proc_info* proc_info,
|
||||
int need_unwind_info, void* arg) {
|
||||
BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
|
||||
bool result = backtrace->FindProcInfo(addr_space, ip, proc_info, need_unwind_info);
|
||||
return result ? 0 : -UNW_EINVAL;
|
||||
}
|
||||
|
||||
static void PutUnwindInfo(unw_addr_space_t, unw_proc_info_t*, void*) {
|
||||
}
|
||||
|
||||
static int GetDynInfoListAddr(unw_addr_space_t, unw_word_t*, void*) {
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
static int AccessMem(unw_addr_space_t, unw_word_t addr, unw_word_t* value, int write, void* arg) {
|
||||
if (write == 1) {
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
|
||||
*value = 0;
|
||||
size_t read_size = backtrace->Read(addr, reinterpret_cast<uint8_t*>(value), sizeof(unw_word_t));
|
||||
// Strictly we should check if read_size matches sizeof(unw_word_t), but it is possible in
|
||||
// .eh_frame_hdr that the section can end at a position not aligned in sizeof(unw_word_t), and
|
||||
// we should permit the read at the end of the section.
|
||||
return (read_size > 0u ? 0 : -UNW_EINVAL);
|
||||
}
|
||||
|
||||
static int AccessReg(unw_addr_space_t, unw_regnum_t unwind_reg, unw_word_t* value, int write,
|
||||
void* arg) {
|
||||
if (write == 1) {
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
|
||||
uint64_t reg_value;
|
||||
bool result = backtrace->ReadReg(unwind_reg, ®_value);
|
||||
if (result) {
|
||||
*value = static_cast<unw_word_t>(reg_value);
|
||||
}
|
||||
return result ? 0 : -UNW_EINVAL;
|
||||
}
|
||||
|
||||
static int AccessFpReg(unw_addr_space_t, unw_regnum_t, unw_fpreg_t*, int, void*) {
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
static int Resume(unw_addr_space_t, unw_cursor_t*, void*) {
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
static int GetProcName(unw_addr_space_t, unw_word_t, char*, size_t, unw_word_t*, void*) {
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
static unw_accessors_t accessors = {
|
||||
.find_proc_info = FindProcInfo,
|
||||
.put_unwind_info = PutUnwindInfo,
|
||||
.get_dyn_info_list_addr = GetDynInfoListAddr,
|
||||
.access_mem = AccessMem,
|
||||
.access_reg = AccessReg,
|
||||
.access_fpreg = AccessFpReg,
|
||||
.resume = Resume,
|
||||
.get_proc_name = GetProcName,
|
||||
};
|
||||
|
||||
bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
|
||||
if (context == nullptr) {
|
||||
BACK_LOGW("The context is needed for offline backtracing.");
|
||||
error_ = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
|
||||
return false;
|
||||
}
|
||||
context_ = context;
|
||||
error_ = BACKTRACE_UNWIND_NO_ERROR;
|
||||
|
||||
unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0);
|
||||
unw_cursor_t cursor;
|
||||
int ret = unw_init_remote(&cursor, addr_space, this);
|
||||
if (ret != 0) {
|
||||
BACK_LOGW("unw_init_remote failed %d", ret);
|
||||
unw_destroy_addr_space(addr_space);
|
||||
error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
|
||||
return false;
|
||||
}
|
||||
size_t num_frames = 0;
|
||||
do {
|
||||
unw_word_t pc;
|
||||
ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("Failed to read IP %d", ret);
|
||||
break;
|
||||
}
|
||||
unw_word_t sp;
|
||||
ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("Failed to read SP %d", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_ignore_frames == 0) {
|
||||
frames_.resize(num_frames + 1);
|
||||
backtrace_frame_data_t* frame = &frames_[num_frames];
|
||||
frame->num = num_frames;
|
||||
frame->pc = static_cast<uintptr_t>(pc);
|
||||
frame->sp = static_cast<uintptr_t>(sp);
|
||||
frame->stack_size = 0;
|
||||
|
||||
if (num_frames > 0) {
|
||||
backtrace_frame_data_t* prev = &frames_[num_frames - 1];
|
||||
prev->stack_size = frame->sp - prev->sp;
|
||||
}
|
||||
frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
|
||||
FillInMap(frame->pc, &frame->map);
|
||||
num_frames++;
|
||||
} else {
|
||||
num_ignore_frames--;
|
||||
}
|
||||
ret = unw_step(&cursor);
|
||||
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
|
||||
|
||||
unw_destroy_addr_space(addr_space);
|
||||
context_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BacktraceOffline::ReadWord(uintptr_t ptr, word_t* out_value) {
|
||||
size_t bytes_read = Read(ptr, reinterpret_cast<uint8_t*>(out_value), sizeof(word_t));
|
||||
return bytes_read == sizeof(word_t);
|
||||
}
|
||||
|
||||
size_t BacktraceOffline::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
|
||||
// Normally, libunwind needs stack information and call frame information to do remote unwinding.
|
||||
// If call frame information is stored in .debug_frame, libunwind can read it from file
|
||||
// by itself. If call frame information is stored in .eh_frame, we need to provide data in
|
||||
// .eh_frame/.eh_frame_hdr sections.
|
||||
// The order of readings below doesn't matter, as the spaces don't overlap with each other.
|
||||
size_t read_size = eh_frame_hdr_space_.Read(addr, buffer, bytes);
|
||||
if (read_size != 0) {
|
||||
return read_size;
|
||||
}
|
||||
read_size = eh_frame_space_.Read(addr, buffer, bytes);
|
||||
if (read_size != 0) {
|
||||
return read_size;
|
||||
}
|
||||
read_size = stack_space_.Read(addr, buffer, bytes);
|
||||
return read_size;
|
||||
}
|
||||
|
||||
static bool FileOffsetToVaddr(
|
||||
const std::vector<DebugFrameInfo::EhFrame::ProgramHeader>& program_headers,
|
||||
uint64_t file_offset, uint64_t* vaddr) {
|
||||
for (auto& header : program_headers) {
|
||||
if (file_offset >= header.file_offset && file_offset < header.file_offset + header.file_size) {
|
||||
// TODO: Consider load_bias?
|
||||
*vaddr = file_offset - header.file_offset + header.vaddr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BacktraceOffline::FindProcInfo(unw_addr_space_t addr_space, uint64_t ip,
|
||||
unw_proc_info_t* proc_info, int need_unwind_info) {
|
||||
backtrace_map_t map;
|
||||
FillInMap(ip, &map);
|
||||
if (!BacktraceMap::IsValid(map)) {
|
||||
return false;
|
||||
}
|
||||
const std::string& filename = map.name;
|
||||
DebugFrameInfo* debug_frame = GetDebugFrameInFile(filename);
|
||||
if (debug_frame == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (debug_frame->is_eh_frame) {
|
||||
uint64_t ip_offset = ip - map.start + map.offset;
|
||||
uint64_t ip_vaddr; // vaddr in the elf file.
|
||||
bool result = FileOffsetToVaddr(debug_frame->eh_frame.program_headers, ip_offset, &ip_vaddr);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
// Calculate the addresses where .eh_frame_hdr and .eh_frame stay when the process was running.
|
||||
eh_frame_hdr_space_.start = (ip - ip_vaddr) + debug_frame->eh_frame.eh_frame_hdr_vaddr;
|
||||
eh_frame_hdr_space_.end =
|
||||
eh_frame_hdr_space_.start + debug_frame->eh_frame.eh_frame_hdr_data.size();
|
||||
eh_frame_hdr_space_.data = debug_frame->eh_frame.eh_frame_hdr_data.data();
|
||||
|
||||
eh_frame_space_.start = (ip - ip_vaddr) + debug_frame->eh_frame.eh_frame_vaddr;
|
||||
eh_frame_space_.end = eh_frame_space_.start + debug_frame->eh_frame.eh_frame_data.size();
|
||||
eh_frame_space_.data = debug_frame->eh_frame.eh_frame_data.data();
|
||||
|
||||
unw_dyn_info di;
|
||||
memset(&di, '\0', sizeof(di));
|
||||
di.start_ip = map.start;
|
||||
di.end_ip = map.end;
|
||||
di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
|
||||
di.u.rti.name_ptr = 0;
|
||||
di.u.rti.segbase = eh_frame_hdr_space_.start;
|
||||
di.u.rti.table_data =
|
||||
eh_frame_hdr_space_.start + debug_frame->eh_frame.fde_table_offset_in_eh_frame_hdr;
|
||||
di.u.rti.table_len = (eh_frame_hdr_space_.end - di.u.rti.table_data) / sizeof(unw_word_t);
|
||||
int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
eh_frame_hdr_space_.Clear();
|
||||
eh_frame_space_.Clear();
|
||||
unw_dyn_info_t di;
|
||||
unw_word_t segbase = map.start - map.offset;
|
||||
int found = dwarf_find_debug_frame(0, &di, ip, segbase, filename.c_str(), map.start, map.end);
|
||||
if (found == 1) {
|
||||
int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
|
||||
return ret == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BacktraceOffline::ReadReg(size_t reg, uint64_t* value) {
|
||||
bool result = true;
|
||||
#if defined(__arm__)
|
||||
switch (reg) {
|
||||
case UNW_ARM_R0:
|
||||
*value = context_->uc_mcontext.arm_r0;
|
||||
break;
|
||||
case UNW_ARM_R1:
|
||||
*value = context_->uc_mcontext.arm_r1;
|
||||
break;
|
||||
case UNW_ARM_R2:
|
||||
*value = context_->uc_mcontext.arm_r2;
|
||||
break;
|
||||
case UNW_ARM_R3:
|
||||
*value = context_->uc_mcontext.arm_r3;
|
||||
break;
|
||||
case UNW_ARM_R4:
|
||||
*value = context_->uc_mcontext.arm_r4;
|
||||
break;
|
||||
case UNW_ARM_R5:
|
||||
*value = context_->uc_mcontext.arm_r5;
|
||||
break;
|
||||
case UNW_ARM_R6:
|
||||
*value = context_->uc_mcontext.arm_r6;
|
||||
break;
|
||||
case UNW_ARM_R7:
|
||||
*value = context_->uc_mcontext.arm_r7;
|
||||
break;
|
||||
case UNW_ARM_R8:
|
||||
*value = context_->uc_mcontext.arm_r8;
|
||||
break;
|
||||
case UNW_ARM_R9:
|
||||
*value = context_->uc_mcontext.arm_r9;
|
||||
break;
|
||||
case UNW_ARM_R10:
|
||||
*value = context_->uc_mcontext.arm_r10;
|
||||
break;
|
||||
case UNW_ARM_R11:
|
||||
*value = context_->uc_mcontext.arm_fp;
|
||||
break;
|
||||
case UNW_ARM_R12:
|
||||
*value = context_->uc_mcontext.arm_ip;
|
||||
break;
|
||||
case UNW_ARM_R13:
|
||||
*value = context_->uc_mcontext.arm_sp;
|
||||
break;
|
||||
case UNW_ARM_R14:
|
||||
*value = context_->uc_mcontext.arm_lr;
|
||||
break;
|
||||
case UNW_ARM_R15:
|
||||
*value = context_->uc_mcontext.arm_pc;
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
if (reg <= UNW_AARCH64_PC) {
|
||||
*value = context_->uc_mcontext.regs[reg];
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
#elif defined(__x86_64__)
|
||||
switch (reg) {
|
||||
case UNW_X86_64_R8:
|
||||
*value = context_->uc_mcontext.gregs[REG_R8];
|
||||
break;
|
||||
case UNW_X86_64_R9:
|
||||
*value = context_->uc_mcontext.gregs[REG_R9];
|
||||
break;
|
||||
case UNW_X86_64_R10:
|
||||
*value = context_->uc_mcontext.gregs[REG_R10];
|
||||
break;
|
||||
case UNW_X86_64_R11:
|
||||
*value = context_->uc_mcontext.gregs[REG_R11];
|
||||
break;
|
||||
case UNW_X86_64_R12:
|
||||
*value = context_->uc_mcontext.gregs[REG_R12];
|
||||
break;
|
||||
case UNW_X86_64_R13:
|
||||
*value = context_->uc_mcontext.gregs[REG_R13];
|
||||
break;
|
||||
case UNW_X86_64_R14:
|
||||
*value = context_->uc_mcontext.gregs[REG_R14];
|
||||
break;
|
||||
case UNW_X86_64_R15:
|
||||
*value = context_->uc_mcontext.gregs[REG_R15];
|
||||
break;
|
||||
case UNW_X86_64_RDI:
|
||||
*value = context_->uc_mcontext.gregs[REG_RDI];
|
||||
break;
|
||||
case UNW_X86_64_RSI:
|
||||
*value = context_->uc_mcontext.gregs[REG_RSI];
|
||||
break;
|
||||
case UNW_X86_64_RBP:
|
||||
*value = context_->uc_mcontext.gregs[REG_RBP];
|
||||
break;
|
||||
case UNW_X86_64_RBX:
|
||||
*value = context_->uc_mcontext.gregs[REG_RBX];
|
||||
break;
|
||||
case UNW_X86_64_RDX:
|
||||
*value = context_->uc_mcontext.gregs[REG_RDX];
|
||||
break;
|
||||
case UNW_X86_64_RAX:
|
||||
*value = context_->uc_mcontext.gregs[REG_RAX];
|
||||
break;
|
||||
case UNW_X86_64_RCX:
|
||||
*value = context_->uc_mcontext.gregs[REG_RCX];
|
||||
break;
|
||||
case UNW_X86_64_RSP:
|
||||
*value = context_->uc_mcontext.gregs[REG_RSP];
|
||||
break;
|
||||
case UNW_X86_64_RIP:
|
||||
*value = context_->uc_mcontext.gregs[REG_RIP];
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
#elif defined(__i386__)
|
||||
switch (reg) {
|
||||
case UNW_X86_GS:
|
||||
*value = context_->uc_mcontext.gregs[REG_GS];
|
||||
break;
|
||||
case UNW_X86_FS:
|
||||
*value = context_->uc_mcontext.gregs[REG_FS];
|
||||
break;
|
||||
case UNW_X86_ES:
|
||||
*value = context_->uc_mcontext.gregs[REG_ES];
|
||||
break;
|
||||
case UNW_X86_DS:
|
||||
*value = context_->uc_mcontext.gregs[REG_DS];
|
||||
break;
|
||||
case UNW_X86_EAX:
|
||||
*value = context_->uc_mcontext.gregs[REG_EAX];
|
||||
break;
|
||||
case UNW_X86_EBX:
|
||||
*value = context_->uc_mcontext.gregs[REG_EBX];
|
||||
break;
|
||||
case UNW_X86_ECX:
|
||||
*value = context_->uc_mcontext.gregs[REG_ECX];
|
||||
break;
|
||||
case UNW_X86_EDX:
|
||||
*value = context_->uc_mcontext.gregs[REG_EDX];
|
||||
break;
|
||||
case UNW_X86_ESI:
|
||||
*value = context_->uc_mcontext.gregs[REG_ESI];
|
||||
break;
|
||||
case UNW_X86_EDI:
|
||||
*value = context_->uc_mcontext.gregs[REG_EDI];
|
||||
break;
|
||||
case UNW_X86_EBP:
|
||||
*value = context_->uc_mcontext.gregs[REG_EBP];
|
||||
break;
|
||||
case UNW_X86_EIP:
|
||||
*value = context_->uc_mcontext.gregs[REG_EIP];
|
||||
break;
|
||||
case UNW_X86_ESP:
|
||||
*value = context_->uc_mcontext.gregs[REG_ESP];
|
||||
break;
|
||||
case UNW_X86_TRAPNO:
|
||||
*value = context_->uc_mcontext.gregs[REG_TRAPNO];
|
||||
break;
|
||||
case UNW_X86_CS:
|
||||
*value = context_->uc_mcontext.gregs[REG_CS];
|
||||
break;
|
||||
case UNW_X86_EFLAGS:
|
||||
*value = context_->uc_mcontext.gregs[REG_EFL];
|
||||
break;
|
||||
case UNW_X86_SS:
|
||||
*value = context_->uc_mcontext.gregs[REG_SS];
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string BacktraceOffline::GetFunctionNameRaw(uintptr_t, uintptr_t* offset) {
|
||||
// We don't have enough information to support this. And it is expensive.
|
||||
*offset = 0;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>> BacktraceOffline::debug_frames_;
|
||||
std::unordered_set<std::string> BacktraceOffline::debug_frame_missing_files_;
|
||||
|
||||
static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename);
|
||||
|
||||
DebugFrameInfo* BacktraceOffline::GetDebugFrameInFile(const std::string& filename) {
|
||||
if (cache_file_) {
|
||||
auto it = debug_frames_.find(filename);
|
||||
if (it != debug_frames_.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
if (debug_frame_missing_files_.find(filename) != debug_frame_missing_files_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
DebugFrameInfo* debug_frame = ReadDebugFrameFromFile(filename);
|
||||
if (cache_file_) {
|
||||
if (debug_frame != nullptr) {
|
||||
debug_frames_.emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
|
||||
} else {
|
||||
debug_frame_missing_files_.insert(filename);
|
||||
}
|
||||
} else {
|
||||
if (last_debug_frame_ != nullptr) {
|
||||
delete last_debug_frame_;
|
||||
}
|
||||
last_debug_frame_ = debug_frame;
|
||||
}
|
||||
return debug_frame;
|
||||
}
|
||||
|
||||
static bool OmitEncodedValue(uint8_t encode, const uint8_t*& p) {
|
||||
if (encode == DW_EH_PE_omit) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t format = encode & 0x0f;
|
||||
switch (format) {
|
||||
case DW_EH_PE_ptr:
|
||||
p += sizeof(unw_word_t);
|
||||
break;
|
||||
case DW_EH_PE_uleb128:
|
||||
case DW_EH_PE_sleb128:
|
||||
while ((*p & 0x80) != 0) {
|
||||
++p;
|
||||
}
|
||||
++p;
|
||||
break;
|
||||
case DW_EH_PE_udata2:
|
||||
case DW_EH_PE_sdata2:
|
||||
p += 2;
|
||||
break;
|
||||
case DW_EH_PE_udata4:
|
||||
case DW_EH_PE_sdata4:
|
||||
p += 4;
|
||||
break;
|
||||
case DW_EH_PE_udata8:
|
||||
case DW_EH_PE_sdata8:
|
||||
p += 8;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetFdeTableOffsetInEhFrameHdr(const std::vector<uint8_t>& data,
|
||||
uint64_t* table_offset_in_eh_frame_hdr) {
|
||||
const uint8_t* p = data.data();
|
||||
const uint8_t* end = p + data.size();
|
||||
if (p + 4 > end) {
|
||||
return false;
|
||||
}
|
||||
uint8_t version = *p++;
|
||||
if (version != 1) {
|
||||
return false;
|
||||
}
|
||||
uint8_t eh_frame_ptr_encode = *p++;
|
||||
uint8_t fde_count_encode = *p++;
|
||||
uint8_t fde_table_encode = *p++;
|
||||
|
||||
if (fde_table_encode != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OmitEncodedValue(eh_frame_ptr_encode, p) || !OmitEncodedValue(fde_count_encode, p)) {
|
||||
return false;
|
||||
}
|
||||
if (p >= end) {
|
||||
return false;
|
||||
}
|
||||
*table_offset_in_eh_frame_hdr = p - data.data();
|
||||
return true;
|
||||
}
|
||||
|
||||
using ProgramHeader = DebugFrameInfo::EhFrame::ProgramHeader;
|
||||
|
||||
template <class ELFT>
|
||||
DebugFrameInfo* ReadDebugFrameFromELFFile(const llvm::object::ELFFile<ELFT>* elf) {
|
||||
bool has_eh_frame_hdr = false;
|
||||
uint64_t eh_frame_hdr_vaddr = 0;
|
||||
std::vector<uint8_t> eh_frame_hdr_data;
|
||||
bool has_eh_frame = false;
|
||||
uint64_t eh_frame_vaddr = 0;
|
||||
std::vector<uint8_t> eh_frame_data;
|
||||
|
||||
for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
|
||||
llvm::ErrorOr<llvm::StringRef> name = elf->getSectionName(&*it);
|
||||
if (name) {
|
||||
if (name.get() == ".debug_frame") {
|
||||
DebugFrameInfo* debug_frame = new DebugFrameInfo;
|
||||
debug_frame->is_eh_frame = false;
|
||||
return debug_frame;
|
||||
}
|
||||
if (name.get() == ".eh_frame_hdr") {
|
||||
has_eh_frame_hdr = true;
|
||||
eh_frame_hdr_vaddr = it->sh_addr;
|
||||
llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
|
||||
if (data) {
|
||||
eh_frame_hdr_data.insert(eh_frame_hdr_data.begin(), data->data(),
|
||||
data->data() + data->size());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (name.get() == ".eh_frame") {
|
||||
has_eh_frame = true;
|
||||
eh_frame_vaddr = it->sh_addr;
|
||||
llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
|
||||
if (data) {
|
||||
eh_frame_data.insert(eh_frame_data.begin(), data->data(), data->data() + data->size());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(has_eh_frame_hdr && has_eh_frame)) {
|
||||
return nullptr;
|
||||
}
|
||||
uint64_t fde_table_offset;
|
||||
if (!GetFdeTableOffsetInEhFrameHdr(eh_frame_hdr_data, &fde_table_offset)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<ProgramHeader> program_headers;
|
||||
for (auto it = elf->program_header_begin(); it != elf->program_header_end(); ++it) {
|
||||
ProgramHeader header;
|
||||
header.vaddr = it->p_vaddr;
|
||||
header.file_offset = it->p_offset;
|
||||
header.file_size = it->p_filesz;
|
||||
program_headers.push_back(header);
|
||||
}
|
||||
DebugFrameInfo* debug_frame = new DebugFrameInfo;
|
||||
debug_frame->is_eh_frame = true;
|
||||
debug_frame->eh_frame.eh_frame_hdr_vaddr = eh_frame_hdr_vaddr;
|
||||
debug_frame->eh_frame.eh_frame_vaddr = eh_frame_vaddr;
|
||||
debug_frame->eh_frame.fde_table_offset_in_eh_frame_hdr = fde_table_offset;
|
||||
debug_frame->eh_frame.eh_frame_hdr_data = std::move(eh_frame_hdr_data);
|
||||
debug_frame->eh_frame.eh_frame_data = std::move(eh_frame_data);
|
||||
debug_frame->eh_frame.program_headers = program_headers;
|
||||
return debug_frame;
|
||||
}
|
||||
|
||||
static bool IsValidElfPath(const std::string& filename) {
|
||||
static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
|
||||
|
||||
struct stat st;
|
||||
if (stat(filename.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
FILE* fp = fopen(filename.c_str(), "reb");
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
char buf[4];
|
||||
if (fread(buf, 4, 1, fp) != 1) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
fclose(fp);
|
||||
return memcmp(buf, elf_magic, 4) == 0;
|
||||
}
|
||||
|
||||
static bool IsValidApkPath(const std::string& apk_path) {
|
||||
static const char zip_preamble[] = {0x50, 0x4b, 0x03, 0x04};
|
||||
struct stat st;
|
||||
if (stat(apk_path.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
FILE* fp = fopen(apk_path.c_str(), "reb");
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
char buf[4];
|
||||
if (fread(buf, 4, 1, fp) != 1) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
fclose(fp);
|
||||
return memcmp(buf, zip_preamble, 4) == 0;
|
||||
}
|
||||
|
||||
class ScopedZiparchiveHandle {
|
||||
public:
|
||||
ScopedZiparchiveHandle(ZipArchiveHandle handle) : handle_(handle) {
|
||||
}
|
||||
|
||||
~ScopedZiparchiveHandle() {
|
||||
CloseArchive(handle_);
|
||||
}
|
||||
|
||||
private:
|
||||
ZipArchiveHandle handle_;
|
||||
};
|
||||
|
||||
llvm::object::OwningBinary<llvm::object::Binary> OpenEmbeddedElfFile(const std::string& filename) {
|
||||
llvm::object::OwningBinary<llvm::object::Binary> nothing;
|
||||
size_t pos = filename.find("!/");
|
||||
if (pos == std::string::npos) {
|
||||
return nothing;
|
||||
}
|
||||
std::string apk_file = filename.substr(0, pos);
|
||||
std::string elf_file = filename.substr(pos + 2);
|
||||
if (!IsValidApkPath(apk_file)) {
|
||||
BACK_LOGW("%s is not a valid apk file", apk_file.c_str());
|
||||
return nothing;
|
||||
}
|
||||
ZipArchiveHandle handle;
|
||||
int32_t ret_code = OpenArchive(apk_file.c_str(), &handle);
|
||||
if (ret_code != 0) {
|
||||
CloseArchive(handle);
|
||||
BACK_LOGW("failed to open archive %s: %s", apk_file.c_str(), ErrorCodeString(ret_code));
|
||||
return nothing;
|
||||
}
|
||||
ScopedZiparchiveHandle scoped_handle(handle);
|
||||
ZipEntry zentry;
|
||||
ret_code = FindEntry(handle, ZipString(elf_file.c_str()), &zentry);
|
||||
if (ret_code != 0) {
|
||||
BACK_LOGW("failed to find %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
|
||||
ErrorCodeString(ret_code));
|
||||
return nothing;
|
||||
}
|
||||
if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) {
|
||||
BACK_LOGW("%s is compressed in %s, which doesn't support running directly", elf_file.c_str(),
|
||||
apk_file.c_str());
|
||||
return nothing;
|
||||
}
|
||||
auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(GetFileDescriptor(handle), apk_file,
|
||||
zentry.uncompressed_length,
|
||||
zentry.offset);
|
||||
if (!buffer_or_err) {
|
||||
BACK_LOGW("failed to read %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
|
||||
buffer_or_err.getError().message().c_str());
|
||||
return nothing;
|
||||
}
|
||||
auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef());
|
||||
if (!binary_or_err) {
|
||||
BACK_LOGW("failed to create binary for %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
|
||||
binary_or_err.getError().message().c_str());
|
||||
return nothing;
|
||||
}
|
||||
return llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
|
||||
std::move(buffer_or_err.get()));
|
||||
}
|
||||
|
||||
static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) {
|
||||
llvm::object::OwningBinary<llvm::object::Binary> owning_binary;
|
||||
if (filename.find("!/") != std::string::npos) {
|
||||
owning_binary = OpenEmbeddedElfFile(filename);
|
||||
} else {
|
||||
if (!IsValidElfPath(filename)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto binary_or_err = llvm::object::createBinary(llvm::StringRef(filename));
|
||||
if (!binary_or_err) {
|
||||
return nullptr;
|
||||
}
|
||||
owning_binary = std::move(binary_or_err.get());
|
||||
}
|
||||
llvm::object::Binary* binary = owning_binary.getBinary();
|
||||
auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
|
||||
if (obj == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
|
||||
return ReadDebugFrameFromELFFile(elf->getELFFile());
|
||||
}
|
||||
if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
|
||||
return ReadDebugFrameFromELFFile(elf->getELFFile());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Backtrace* Backtrace::CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
|
||||
const backtrace_stackinfo_t& stack, bool cache_file) {
|
||||
return new BacktraceOffline(pid, tid, map, stack, cache_file);
|
||||
}
|
||||
@@ -1,105 +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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_UNWIND_OFFLINE_H
|
||||
#define _LIBBACKTRACE_UNWIND_OFFLINE_H
|
||||
|
||||
#include <libunwind.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
struct Space {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
const uint8_t* data;
|
||||
|
||||
Space() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Clear();
|
||||
size_t Read(uint64_t addr, uint8_t* buffer, size_t size);
|
||||
};
|
||||
|
||||
struct DebugFrameInfo {
|
||||
bool is_eh_frame;
|
||||
struct EhFrame {
|
||||
uint64_t eh_frame_hdr_vaddr;
|
||||
uint64_t eh_frame_vaddr;
|
||||
uint64_t fde_table_offset_in_eh_frame_hdr;
|
||||
std::vector<uint8_t> eh_frame_hdr_data;
|
||||
std::vector<uint8_t> eh_frame_data;
|
||||
struct ProgramHeader {
|
||||
uint64_t vaddr;
|
||||
uint64_t file_offset;
|
||||
uint64_t file_size;
|
||||
};
|
||||
std::vector<ProgramHeader> program_headers;
|
||||
} eh_frame;
|
||||
};
|
||||
|
||||
class BacktraceOffline : public Backtrace {
|
||||
public:
|
||||
BacktraceOffline(pid_t pid, pid_t tid, BacktraceMap* map, const backtrace_stackinfo_t& stack,
|
||||
bool cache_file)
|
||||
: Backtrace(pid, tid, map),
|
||||
cache_file_(cache_file),
|
||||
context_(nullptr),
|
||||
last_debug_frame_(nullptr) {
|
||||
stack_space_.start = stack.start;
|
||||
stack_space_.end = stack.end;
|
||||
stack_space_.data = stack.data;
|
||||
}
|
||||
|
||||
virtual ~BacktraceOffline() {
|
||||
if (last_debug_frame_ != nullptr) {
|
||||
delete last_debug_frame_;
|
||||
}
|
||||
}
|
||||
|
||||
bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
|
||||
|
||||
bool ReadWord(uintptr_t ptr, word_t* out_value) override;
|
||||
|
||||
size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
|
||||
|
||||
bool FindProcInfo(unw_addr_space_t addr_space, uint64_t ip, unw_proc_info_t* proc_info,
|
||||
int need_unwind_info);
|
||||
|
||||
bool ReadReg(size_t reg_index, uint64_t* value);
|
||||
|
||||
protected:
|
||||
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
|
||||
DebugFrameInfo* GetDebugFrameInFile(const std::string& filename);
|
||||
|
||||
static std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>> debug_frames_;
|
||||
static std::unordered_set<std::string> debug_frame_missing_files_;
|
||||
|
||||
bool cache_file_;
|
||||
ucontext_t* context_;
|
||||
Space eh_frame_hdr_space_;
|
||||
Space eh_frame_space_;
|
||||
Space stack_space_;
|
||||
DebugFrameInfo* last_debug_frame_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_OFFLINE_H
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "BacktracePtrace.h"
|
||||
#include "thread_utils.h"
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
static bool PtraceRead(pid_t tid, uintptr_t addr, word_t* out_value) {
|
||||
// ptrace() returns -1 and sets errno when the operation fails.
|
||||
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
||||
errno = 0;
|
||||
*out_value = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(addr), nullptr);
|
||||
if (*out_value == static_cast<word_t>(-1) && errno) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) {
|
||||
#if defined(__APPLE__)
|
||||
BACK_LOGW("MacOS does not support reading from another pid.");
|
||||
return false;
|
||||
#else
|
||||
if (!VerifyReadWordArgs(ptr, out_value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
backtrace_map_t map;
|
||||
FillInMap(ptr, &map);
|
||||
if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return PtraceRead(Tid(), ptr, out_value);
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t BacktracePtrace::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
|
||||
#if defined(__APPLE__)
|
||||
BACK_LOGW("MacOS does not support reading from another pid.");
|
||||
return 0;
|
||||
#else
|
||||
backtrace_map_t map;
|
||||
FillInMap(addr, &map);
|
||||
if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytes = MIN(map.end - addr, bytes);
|
||||
size_t bytes_read = 0;
|
||||
word_t data_word;
|
||||
size_t align_bytes = addr & (sizeof(word_t) - 1);
|
||||
if (align_bytes != 0) {
|
||||
if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) {
|
||||
return 0;
|
||||
}
|
||||
size_t copy_bytes = MIN(sizeof(word_t) - align_bytes, bytes);
|
||||
memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + align_bytes, copy_bytes);
|
||||
addr += copy_bytes;
|
||||
buffer += copy_bytes;
|
||||
bytes -= copy_bytes;
|
||||
bytes_read += copy_bytes;
|
||||
}
|
||||
|
||||
size_t num_words = bytes / sizeof(word_t);
|
||||
for (size_t i = 0; i < num_words; i++) {
|
||||
if (!PtraceRead(Tid(), addr, &data_word)) {
|
||||
return bytes_read;
|
||||
}
|
||||
memcpy(buffer, &data_word, sizeof(word_t));
|
||||
buffer += sizeof(word_t);
|
||||
addr += sizeof(word_t);
|
||||
bytes_read += sizeof(word_t);
|
||||
}
|
||||
|
||||
size_t left_over = bytes & (sizeof(word_t) - 1);
|
||||
if (left_over) {
|
||||
if (!PtraceRead(Tid(), addr, &data_word)) {
|
||||
return bytes_read;
|
||||
}
|
||||
memcpy(buffer, &data_word, left_over);
|
||||
bytes_read += left_over;
|
||||
}
|
||||
return bytes_read;
|
||||
#endif
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 _LIBBACKTRACE_BACKTRACE_PTRACE_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_PTRACE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
class BacktraceMap;
|
||||
|
||||
class BacktracePtrace : public Backtrace {
|
||||
public:
|
||||
BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
|
||||
virtual ~BacktracePtrace() {}
|
||||
|
||||
size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes);
|
||||
|
||||
bool ReadWord(uintptr_t ptr, word_t* out_value);
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_PTRACE_H
|
||||
@@ -1,93 +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 <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// This is an extremely simplified version of libpagemap.
|
||||
|
||||
#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1))
|
||||
|
||||
#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1))
|
||||
#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1))
|
||||
#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6))
|
||||
#define PAGEMAP_PFN(x) (_BITS(x, 0, 55))
|
||||
#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
|
||||
#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5))
|
||||
|
||||
static bool ReadData(int fd, unsigned long place, uint64_t *data) {
|
||||
if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
|
||||
return false;
|
||||
}
|
||||
if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t GetPssBytes() {
|
||||
FILE* maps = fopen("/proc/self/maps", "r");
|
||||
if (maps == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
|
||||
if (pagecount_fd == -1) {
|
||||
fclose(maps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
|
||||
if (pagemap_fd == -1) {
|
||||
fclose(maps);
|
||||
close(pagecount_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char line[4096];
|
||||
size_t total_pss = 0;
|
||||
int pagesize = getpagesize();
|
||||
while (fgets(line, sizeof(line), maps)) {
|
||||
uintptr_t start, end;
|
||||
if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
|
||||
total_pss = 0;
|
||||
break;
|
||||
}
|
||||
for (size_t page = start/pagesize; page < end/pagesize; page++) {
|
||||
uint64_t data;
|
||||
if (ReadData(pagemap_fd, page, &data)) {
|
||||
if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
|
||||
uint64_t count;
|
||||
if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) {
|
||||
total_pss += (count >= 1) ? pagesize / count : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(maps);
|
||||
|
||||
close(pagecount_fd);
|
||||
close(pagemap_fd);
|
||||
|
||||
return total_pss;
|
||||
}
|
||||
@@ -1,22 +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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_GET_PSS_H
|
||||
#define _LIBBACKTRACE_GET_PSS_H
|
||||
|
||||
size_t GetPssBytes();
|
||||
|
||||
#endif // _LIBBACKTRACE_GET_PSS_H
|
||||
@@ -1,131 +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 <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "ThreadEntry.h"
|
||||
|
||||
// Initialize static member variables.
|
||||
ThreadEntry* ThreadEntry::list_ = nullptr;
|
||||
pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Assumes that ThreadEntry::list_mutex_ has already been locked before
|
||||
// creating a ThreadEntry object.
|
||||
ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
|
||||
: pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
|
||||
wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
|
||||
next_(ThreadEntry::list_), prev_(nullptr) {
|
||||
pthread_condattr_t attr;
|
||||
pthread_condattr_init(&attr);
|
||||
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
|
||||
pthread_cond_init(&wait_cond_, &attr);
|
||||
|
||||
// Add ourselves to the list.
|
||||
if (ThreadEntry::list_) {
|
||||
ThreadEntry::list_->prev_ = this;
|
||||
}
|
||||
ThreadEntry::list_ = this;
|
||||
}
|
||||
|
||||
ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
|
||||
pthread_mutex_lock(&ThreadEntry::list_mutex_);
|
||||
ThreadEntry* entry = list_;
|
||||
while (entry != nullptr) {
|
||||
if (entry->Match(pid, tid)) {
|
||||
break;
|
||||
}
|
||||
entry = entry->next_;
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
if (create) {
|
||||
entry = new ThreadEntry(pid, tid);
|
||||
}
|
||||
} else {
|
||||
entry->ref_count_++;
|
||||
}
|
||||
pthread_mutex_unlock(&ThreadEntry::list_mutex_);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void ThreadEntry::Remove(ThreadEntry* entry) {
|
||||
entry->Unlock();
|
||||
|
||||
pthread_mutex_lock(&ThreadEntry::list_mutex_);
|
||||
if (--entry->ref_count_ == 0) {
|
||||
delete entry;
|
||||
}
|
||||
pthread_mutex_unlock(&ThreadEntry::list_mutex_);
|
||||
}
|
||||
|
||||
// Assumes that ThreadEntry::list_mutex_ has already been locked before
|
||||
// deleting a ThreadEntry object.
|
||||
ThreadEntry::~ThreadEntry() {
|
||||
if (list_ == this) {
|
||||
list_ = next_;
|
||||
} else {
|
||||
if (next_) {
|
||||
next_->prev_ = prev_;
|
||||
}
|
||||
prev_->next_ = next_;
|
||||
}
|
||||
|
||||
next_ = nullptr;
|
||||
prev_ = nullptr;
|
||||
|
||||
pthread_cond_destroy(&wait_cond_);
|
||||
}
|
||||
|
||||
bool ThreadEntry::Wait(int value) {
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
ts.tv_sec += 5;
|
||||
|
||||
bool wait_completed = true;
|
||||
pthread_mutex_lock(&wait_mutex_);
|
||||
while (wait_value_ != value) {
|
||||
int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
|
||||
if (ret != 0) {
|
||||
BACK_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
|
||||
wait_completed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&wait_mutex_);
|
||||
|
||||
return wait_completed;
|
||||
}
|
||||
|
||||
void ThreadEntry::Wake() {
|
||||
pthread_mutex_lock(&wait_mutex_);
|
||||
wait_value_++;
|
||||
pthread_mutex_unlock(&wait_mutex_);
|
||||
|
||||
pthread_cond_signal(&wait_cond_);
|
||||
}
|
||||
|
||||
void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
|
||||
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
|
||||
// The only thing the unwinder cares about is the mcontext data.
|
||||
memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 _LIBBACKTRACE_THREAD_ENTRY_H
|
||||
#define _LIBBACKTRACE_THREAD_ENTRY_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
class ThreadEntry {
|
||||
public:
|
||||
static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true);
|
||||
|
||||
static void Remove(ThreadEntry* entry);
|
||||
|
||||
void Wake();
|
||||
|
||||
bool Wait(int);
|
||||
|
||||
void CopyUcontextFromSigcontext(void*);
|
||||
|
||||
inline void Lock() {
|
||||
pthread_mutex_lock(&mutex_);
|
||||
|
||||
// Always reset the wait value since this could be the first or nth
|
||||
// time this entry is locked.
|
||||
wait_value_ = 0;
|
||||
}
|
||||
|
||||
inline void Unlock() {
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
inline ucontext_t* GetUcontext() { return &ucontext_; }
|
||||
|
||||
private:
|
||||
ThreadEntry(pid_t pid, pid_t tid);
|
||||
~ThreadEntry();
|
||||
|
||||
bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); }
|
||||
|
||||
pid_t pid_;
|
||||
pid_t tid_;
|
||||
int ref_count_;
|
||||
pthread_mutex_t mutex_;
|
||||
pthread_mutex_t wait_mutex_;
|
||||
pthread_cond_t wait_cond_;
|
||||
int wait_value_;
|
||||
ThreadEntry* next_;
|
||||
ThreadEntry* prev_;
|
||||
ucontext_t ucontext_;
|
||||
|
||||
static ThreadEntry* list_;
|
||||
static pthread_mutex_t list_mutex_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_THREAD_ENTRY_H
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <stdint.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "UnwindCurrent.h"
|
||||
|
||||
std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
|
||||
*offset = 0;
|
||||
char buf[512];
|
||||
unw_word_t value;
|
||||
if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
|
||||
&value, &context_) >= 0 && buf[0] != '\0') {
|
||||
*offset = static_cast<uintptr_t>(value);
|
||||
return buf;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) {
|
||||
unw_tdep_context_t* unw_context = reinterpret_cast<unw_tdep_context_t*>(&context_);
|
||||
|
||||
#if defined(__arm__)
|
||||
unw_context->regs[0] = ucontext->uc_mcontext.arm_r0;
|
||||
unw_context->regs[1] = ucontext->uc_mcontext.arm_r1;
|
||||
unw_context->regs[2] = ucontext->uc_mcontext.arm_r2;
|
||||
unw_context->regs[3] = ucontext->uc_mcontext.arm_r3;
|
||||
unw_context->regs[4] = ucontext->uc_mcontext.arm_r4;
|
||||
unw_context->regs[5] = ucontext->uc_mcontext.arm_r5;
|
||||
unw_context->regs[6] = ucontext->uc_mcontext.arm_r6;
|
||||
unw_context->regs[7] = ucontext->uc_mcontext.arm_r7;
|
||||
unw_context->regs[8] = ucontext->uc_mcontext.arm_r8;
|
||||
unw_context->regs[9] = ucontext->uc_mcontext.arm_r9;
|
||||
unw_context->regs[10] = ucontext->uc_mcontext.arm_r10;
|
||||
unw_context->regs[11] = ucontext->uc_mcontext.arm_fp;
|
||||
unw_context->regs[12] = ucontext->uc_mcontext.arm_ip;
|
||||
unw_context->regs[13] = ucontext->uc_mcontext.arm_sp;
|
||||
unw_context->regs[14] = ucontext->uc_mcontext.arm_lr;
|
||||
unw_context->regs[15] = ucontext->uc_mcontext.arm_pc;
|
||||
#else
|
||||
unw_context->uc_mcontext = ucontext->uc_mcontext;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
|
||||
if (ucontext == nullptr) {
|
||||
int ret = unw_getcontext(&context_);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("unw_getcontext failed %d", ret);
|
||||
error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
GetUnwContextFromUcontext(ucontext);
|
||||
}
|
||||
|
||||
// The cursor structure is pretty large, do not put it on the stack.
|
||||
std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t);
|
||||
int ret = unw_init_local(cursor.get(), &context_);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("unw_init_local failed %d", ret);
|
||||
error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_frames = 0;
|
||||
do {
|
||||
unw_word_t pc;
|
||||
ret = unw_get_reg(cursor.get(), UNW_REG_IP, &pc);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("Failed to read IP %d", ret);
|
||||
break;
|
||||
}
|
||||
unw_word_t sp;
|
||||
ret = unw_get_reg(cursor.get(), UNW_REG_SP, &sp);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("Failed to read SP %d", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
frames_.resize(num_frames+1);
|
||||
backtrace_frame_data_t* frame = &frames_.at(num_frames);
|
||||
frame->num = num_frames;
|
||||
frame->pc = static_cast<uintptr_t>(pc);
|
||||
frame->sp = static_cast<uintptr_t>(sp);
|
||||
frame->stack_size = 0;
|
||||
|
||||
FillInMap(frame->pc, &frame->map);
|
||||
// Check to see if we should skip this frame because it's coming
|
||||
// from within the library, and we are doing a local unwind.
|
||||
if (ucontext != nullptr || num_frames != 0 || !DiscardFrame(*frame)) {
|
||||
if (num_ignore_frames == 0) {
|
||||
// GetFunctionName is an expensive call, only do it if we are
|
||||
// keeping the frame.
|
||||
frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
|
||||
if (num_frames > 0) {
|
||||
// Set the stack size for the previous frame.
|
||||
backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
|
||||
prev->stack_size = frame->sp - prev->sp;
|
||||
}
|
||||
num_frames++;
|
||||
} else {
|
||||
num_ignore_frames--;
|
||||
}
|
||||
}
|
||||
ret = unw_step (cursor.get());
|
||||
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 _LIBBACKTRACE_UNWIND_CURRENT_H
|
||||
#define _LIBBACKTRACE_UNWIND_CURRENT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include "BacktraceCurrent.h"
|
||||
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
|
||||
class UnwindCurrent : public BacktraceCurrent {
|
||||
public:
|
||||
UnwindCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : BacktraceCurrent(pid, tid, map) {}
|
||||
virtual ~UnwindCurrent() {}
|
||||
|
||||
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
|
||||
|
||||
private:
|
||||
void GetUnwContextFromUcontext(const ucontext_t* ucontext);
|
||||
|
||||
bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
|
||||
|
||||
unw_context_t context_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_CURRENT_H
|
||||
@@ -1,157 +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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include <libunwind.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "UnwindMap.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// libunwind has a single shared address space for the current process
|
||||
// aka local. If multiple maps are created for the current pid, then
|
||||
// only update the local address space once, and keep a reference count
|
||||
// of maps using the same map cursor.
|
||||
//-------------------------------------------------------------------------
|
||||
UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
|
||||
unw_map_cursor_clear(&map_cursor_);
|
||||
}
|
||||
|
||||
UnwindMapRemote::UnwindMapRemote(pid_t pid) : UnwindMap(pid) {
|
||||
}
|
||||
|
||||
UnwindMapRemote::~UnwindMapRemote() {
|
||||
unw_map_cursor_destroy(&map_cursor_);
|
||||
unw_map_cursor_clear(&map_cursor_);
|
||||
}
|
||||
|
||||
bool UnwindMapRemote::GenerateMap() {
|
||||
// Use the map_cursor information to construct the BacktraceMap data
|
||||
// rather than reparsing /proc/self/maps.
|
||||
unw_map_cursor_reset(&map_cursor_);
|
||||
|
||||
unw_map_t unw_map;
|
||||
while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) {
|
||||
backtrace_map_t map;
|
||||
|
||||
map.start = unw_map.start;
|
||||
map.end = unw_map.end;
|
||||
map.offset = unw_map.offset;
|
||||
map.load_base = unw_map.load_base;
|
||||
map.flags = unw_map.flags;
|
||||
map.name = unw_map.path;
|
||||
|
||||
// The maps are in descending order, but we want them in ascending order.
|
||||
maps_.push_front(map);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnwindMapRemote::Build() {
|
||||
return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
|
||||
}
|
||||
|
||||
UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
|
||||
}
|
||||
|
||||
UnwindMapLocal::~UnwindMapLocal() {
|
||||
if (map_created_) {
|
||||
unw_map_local_destroy();
|
||||
unw_map_cursor_clear(&map_cursor_);
|
||||
}
|
||||
}
|
||||
|
||||
bool UnwindMapLocal::GenerateMap() {
|
||||
// It's possible for the map to be regenerated while this loop is occurring.
|
||||
// If that happens, get the map again, but only try at most three times
|
||||
// before giving up.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
maps_.clear();
|
||||
|
||||
// Save the map data retrieved so we can tell if it changes.
|
||||
unw_map_local_cursor_get(&map_cursor_);
|
||||
|
||||
unw_map_t unw_map;
|
||||
int ret;
|
||||
while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) {
|
||||
backtrace_map_t map;
|
||||
|
||||
map.start = unw_map.start;
|
||||
map.end = unw_map.end;
|
||||
map.offset = unw_map.offset;
|
||||
map.load_base = unw_map.load_base;
|
||||
map.flags = unw_map.flags;
|
||||
map.name = unw_map.path;
|
||||
|
||||
free(unw_map.path);
|
||||
|
||||
// The maps are in descending order, but we want them in ascending order.
|
||||
maps_.push_front(map);
|
||||
}
|
||||
// Check to see if the map changed while getting the data.
|
||||
if (ret != -UNW_EINVAL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
BACK_LOGW("Unable to generate the map.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnwindMapLocal::Build() {
|
||||
return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
|
||||
}
|
||||
|
||||
void UnwindMapLocal::FillIn(uintptr_t addr, backtrace_map_t* map) {
|
||||
BacktraceMap::FillIn(addr, map);
|
||||
if (!IsValid(*map)) {
|
||||
// Check to see if the underlying map changed and regenerate the map
|
||||
// if it did.
|
||||
if (unw_map_local_cursor_valid(&map_cursor_) < 0) {
|
||||
if (GenerateMap()) {
|
||||
BacktraceMap::FillIn(addr, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// BacktraceMap create function.
|
||||
//-------------------------------------------------------------------------
|
||||
BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
|
||||
BacktraceMap* map;
|
||||
|
||||
if (uncached) {
|
||||
// Force use of the base class to parse the maps when this call is made.
|
||||
map = new BacktraceMap(pid);
|
||||
} else if (pid == getpid()) {
|
||||
map = new UnwindMapLocal();
|
||||
} else {
|
||||
map = new UnwindMapRemote(pid);
|
||||
}
|
||||
if (!map->Build()) {
|
||||
delete map;
|
||||
return nullptr;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@@ -1,65 +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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_UNWIND_MAP_H
|
||||
#define _LIBBACKTRACE_UNWIND_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
// The unw_map_cursor_t structure is different depending on whether it is
|
||||
// the local or remote version. In order to get the correct version, include
|
||||
// libunwind.h first then this header.
|
||||
|
||||
class UnwindMap : public BacktraceMap {
|
||||
public:
|
||||
UnwindMap(pid_t pid);
|
||||
|
||||
unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
|
||||
|
||||
protected:
|
||||
unw_map_cursor_t map_cursor_;
|
||||
};
|
||||
|
||||
class UnwindMapRemote : public UnwindMap {
|
||||
public:
|
||||
UnwindMapRemote(pid_t pid);
|
||||
virtual ~UnwindMapRemote();
|
||||
|
||||
bool Build() override;
|
||||
|
||||
private:
|
||||
bool GenerateMap();
|
||||
};
|
||||
|
||||
class UnwindMapLocal : public UnwindMap {
|
||||
public:
|
||||
UnwindMapLocal();
|
||||
virtual ~UnwindMapLocal();
|
||||
|
||||
bool Build() override;
|
||||
|
||||
void FillIn(uintptr_t addr, backtrace_map_t* map) override;
|
||||
|
||||
private:
|
||||
bool GenerateMap();
|
||||
|
||||
bool map_created_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_MAP_H
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <libunwind.h>
|
||||
#include <libunwind-ptrace.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "UnwindMap.h"
|
||||
#include "UnwindPtrace.h"
|
||||
|
||||
UnwindPtrace::UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: BacktracePtrace(pid, tid, map), addr_space_(nullptr), upt_info_(nullptr) {
|
||||
}
|
||||
|
||||
UnwindPtrace::~UnwindPtrace() {
|
||||
if (upt_info_) {
|
||||
_UPT_destroy(upt_info_);
|
||||
upt_info_ = nullptr;
|
||||
}
|
||||
if (addr_space_) {
|
||||
// Remove the map from the address space before destroying it.
|
||||
// It will be freed in the UnwindMap destructor.
|
||||
unw_map_set(addr_space_, nullptr);
|
||||
|
||||
unw_destroy_addr_space(addr_space_);
|
||||
addr_space_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
|
||||
if (GetMap() == nullptr) {
|
||||
// Without a map object, we can't do anything.
|
||||
error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
|
||||
return false;
|
||||
}
|
||||
|
||||
error_ = BACKTRACE_UNWIND_NO_ERROR;
|
||||
|
||||
if (ucontext) {
|
||||
BACK_LOGW("Unwinding from a specified context not supported yet.");
|
||||
error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
|
||||
return false;
|
||||
}
|
||||
|
||||
addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
|
||||
if (!addr_space_) {
|
||||
BACK_LOGW("unw_create_addr_space failed.");
|
||||
error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
UnwindMap* map = static_cast<UnwindMap*>(GetMap());
|
||||
unw_map_set(addr_space_, map->GetMapCursor());
|
||||
|
||||
upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
|
||||
if (!upt_info_) {
|
||||
BACK_LOGW("Failed to create upt info.");
|
||||
error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
unw_cursor_t cursor;
|
||||
int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("unw_init_remote failed %d", ret);
|
||||
error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_frames = 0;
|
||||
do {
|
||||
unw_word_t pc;
|
||||
ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("Failed to read IP %d", ret);
|
||||
break;
|
||||
}
|
||||
unw_word_t sp;
|
||||
ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
|
||||
if (ret < 0) {
|
||||
BACK_LOGW("Failed to read SP %d", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_ignore_frames == 0) {
|
||||
frames_.resize(num_frames+1);
|
||||
backtrace_frame_data_t* frame = &frames_.at(num_frames);
|
||||
frame->num = num_frames;
|
||||
frame->pc = static_cast<uintptr_t>(pc);
|
||||
frame->sp = static_cast<uintptr_t>(sp);
|
||||
frame->stack_size = 0;
|
||||
|
||||
if (num_frames > 0) {
|
||||
backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
|
||||
prev->stack_size = frame->sp - prev->sp;
|
||||
}
|
||||
|
||||
frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
|
||||
|
||||
FillInMap(frame->pc, &frame->map);
|
||||
|
||||
num_frames++;
|
||||
} else {
|
||||
num_ignore_frames--;
|
||||
}
|
||||
ret = unw_step (&cursor);
|
||||
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
|
||||
*offset = 0;
|
||||
char buf[512];
|
||||
unw_word_t value;
|
||||
if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value,
|
||||
upt_info_) >= 0 && buf[0] != '\0') {
|
||||
*offset = static_cast<uintptr_t>(value);
|
||||
return buf;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 _LIBBACKTRACE_UNWIND_PTRACE_H
|
||||
#define _LIBBACKTRACE_UNWIND_PTRACE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef UNW_LOCAL_ONLY
|
||||
#undef UNW_LOCAL_ONLY
|
||||
#endif
|
||||
#include <libunwind.h>
|
||||
|
||||
#include "BacktracePtrace.h"
|
||||
|
||||
class UnwindPtrace : public BacktracePtrace {
|
||||
public:
|
||||
UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
|
||||
virtual ~UnwindPtrace();
|
||||
|
||||
bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
|
||||
|
||||
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
|
||||
|
||||
private:
|
||||
unw_addr_space_t addr_space_;
|
||||
struct UPT_info* upt_info_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_PTRACE_H
|
||||
@@ -1,189 +0,0 @@
|
||||
#include <libunwind.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <cutils/threads.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
extern "C" {
|
||||
// Prototypes for functions in the test library.
|
||||
int test_level_one(int, int, int, int, void (*)(void*), void*);
|
||||
int test_level_two(int, int, int, int, void (*)(void*), void*);
|
||||
int test_level_three(int, int, int, int, void (*)(void*), void*);
|
||||
int test_level_four(int, int, int, int, void (*)(void*), void*);
|
||||
int test_recursive_call(int, void (*)(void*), void*);
|
||||
}
|
||||
|
||||
static volatile bool g_exit_flag = false;
|
||||
|
||||
static void GetContextAndExit(void* arg) {
|
||||
unw_context_t* unw_context = reinterpret_cast<unw_context_t*>(arg);
|
||||
unw_getcontext(unw_context);
|
||||
// Don't touch the stack anymore.
|
||||
while (!g_exit_flag) {
|
||||
}
|
||||
}
|
||||
|
||||
struct OfflineThreadArg {
|
||||
unw_context_t unw_context;
|
||||
pid_t tid;
|
||||
std::function<int(void (*)(void*), void*)> function;
|
||||
};
|
||||
|
||||
static void* OfflineThreadFunc(void* arg) {
|
||||
OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
|
||||
fn_arg->tid = gettid();
|
||||
fn_arg->function(GetContextAndExit, &fn_arg->unw_context);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static ucontext_t GetUContextFromUnwContext(const unw_context_t& unw_context) {
|
||||
ucontext_t ucontext;
|
||||
memset(&ucontext, 0, sizeof(ucontext));
|
||||
#if defined(__arm__)
|
||||
ucontext.uc_mcontext.arm_r0 = unw_context.regs[0];
|
||||
ucontext.uc_mcontext.arm_r1 = unw_context.regs[1];
|
||||
ucontext.uc_mcontext.arm_r2 = unw_context.regs[2];
|
||||
ucontext.uc_mcontext.arm_r3 = unw_context.regs[3];
|
||||
ucontext.uc_mcontext.arm_r4 = unw_context.regs[4];
|
||||
ucontext.uc_mcontext.arm_r5 = unw_context.regs[5];
|
||||
ucontext.uc_mcontext.arm_r6 = unw_context.regs[6];
|
||||
ucontext.uc_mcontext.arm_r7 = unw_context.regs[7];
|
||||
ucontext.uc_mcontext.arm_r8 = unw_context.regs[8];
|
||||
ucontext.uc_mcontext.arm_r9 = unw_context.regs[9];
|
||||
ucontext.uc_mcontext.arm_r10 = unw_context.regs[10];
|
||||
ucontext.uc_mcontext.arm_fp = unw_context.regs[11];
|
||||
ucontext.uc_mcontext.arm_ip = unw_context.regs[12];
|
||||
ucontext.uc_mcontext.arm_sp = unw_context.regs[13];
|
||||
ucontext.uc_mcontext.arm_lr = unw_context.regs[14];
|
||||
ucontext.uc_mcontext.arm_pc = unw_context.regs[15];
|
||||
#else
|
||||
ucontext.uc_mcontext = unw_context.uc_mcontext;
|
||||
#endif
|
||||
return ucontext;
|
||||
}
|
||||
|
||||
static void OfflineBacktraceFunctionCall(std::function<int(void (*)(void*), void*)> function,
|
||||
std::vector<uintptr_t>* pc_values) {
|
||||
// Create a thread to generate the needed stack and registers information.
|
||||
g_exit_flag = false;
|
||||
const size_t stack_size = 1024 * 1024;
|
||||
void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
ASSERT_NE(MAP_FAILED, stack);
|
||||
uintptr_t stack_addr = reinterpret_cast<uintptr_t>(stack);
|
||||
pthread_attr_t attr;
|
||||
ASSERT_EQ(0, pthread_attr_init(&attr));
|
||||
ASSERT_EQ(0, pthread_attr_setstack(&attr, reinterpret_cast<void*>(stack), stack_size));
|
||||
pthread_t thread;
|
||||
OfflineThreadArg arg;
|
||||
arg.function = function;
|
||||
ASSERT_EQ(0, pthread_create(&thread, &attr, OfflineThreadFunc, &arg));
|
||||
// Wait for the offline thread to generate the stack and unw_context information.
|
||||
sleep(1);
|
||||
// Copy the stack information.
|
||||
std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
|
||||
reinterpret_cast<uint8_t*>(stack) + stack_size);
|
||||
g_exit_flag = true;
|
||||
ASSERT_EQ(0, pthread_join(thread, nullptr));
|
||||
ASSERT_EQ(0, munmap(stack, stack_size));
|
||||
|
||||
// Do offline backtrace.
|
||||
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
|
||||
ASSERT_TRUE(map != nullptr);
|
||||
|
||||
backtrace_stackinfo_t stack_info;
|
||||
stack_info.start = stack_addr;
|
||||
stack_info.end = stack_addr + stack_size;
|
||||
stack_info.data = stack_data.data();
|
||||
|
||||
std::unique_ptr<Backtrace> backtrace(
|
||||
Backtrace::CreateOffline(getpid(), arg.tid, map.get(), stack_info));
|
||||
ASSERT_TRUE(backtrace != nullptr);
|
||||
|
||||
ucontext_t ucontext = GetUContextFromUnwContext(arg.unw_context);
|
||||
ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
|
||||
|
||||
// Collect pc values of the call stack frames.
|
||||
for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
|
||||
pc_values->push_back(backtrace->GetFrame(i)->pc);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the name of the function which matches the address. Although we don't know the
|
||||
// exact end of each function, it is accurate enough for the tests.
|
||||
static std::string FunctionNameForAddress(uintptr_t addr) {
|
||||
struct FunctionSymbol {
|
||||
std::string name;
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
};
|
||||
|
||||
static std::vector<FunctionSymbol> symbols;
|
||||
if (symbols.empty()) {
|
||||
symbols = std::vector<FunctionSymbol>{
|
||||
{"unknown_start", 0, 0},
|
||||
{"test_level_one", reinterpret_cast<uintptr_t>(&test_level_one), 0},
|
||||
{"test_level_two", reinterpret_cast<uintptr_t>(&test_level_two), 0},
|
||||
{"test_level_three", reinterpret_cast<uintptr_t>(&test_level_three), 0},
|
||||
{"test_level_four", reinterpret_cast<uintptr_t>(&test_level_four), 0},
|
||||
{"test_recursive_call", reinterpret_cast<uintptr_t>(&test_recursive_call), 0},
|
||||
{"GetContextAndExit", reinterpret_cast<uintptr_t>(&GetContextAndExit), 0},
|
||||
{"unknown_end", static_cast<uintptr_t>(-1), static_cast<uintptr_t>(-1)},
|
||||
};
|
||||
std::sort(
|
||||
symbols.begin(), symbols.end(),
|
||||
[](const FunctionSymbol& s1, const FunctionSymbol& s2) { return s1.start < s2.start; });
|
||||
for (size_t i = 0; i + 1 < symbols.size(); ++i) {
|
||||
symbols[i].end = symbols[i + 1].start;
|
||||
}
|
||||
}
|
||||
for (auto& symbol : symbols) {
|
||||
if (addr >= symbol.start && addr < symbol.end) {
|
||||
return symbol.name;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
TEST(libbacktrace, offline) {
|
||||
std::function<int(void (*)(void*), void*)> function =
|
||||
std::bind(test_level_one, 1, 2, 3, 4, std::placeholders::_1, std::placeholders::_2);
|
||||
std::vector<uintptr_t> pc_values;
|
||||
OfflineBacktraceFunctionCall(function, &pc_values);
|
||||
ASSERT_FALSE(pc_values.empty());
|
||||
ASSERT_LE(pc_values.size(), static_cast<size_t>(MAX_BACKTRACE_FRAMES));
|
||||
|
||||
size_t test_one_index = 0;
|
||||
for (size_t i = 0; i < pc_values.size(); ++i) {
|
||||
if (FunctionNameForAddress(pc_values[i]) == "test_level_one") {
|
||||
test_one_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_GE(test_one_index, 3u);
|
||||
ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index]));
|
||||
ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1]));
|
||||
ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2]));
|
||||
ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3]));
|
||||
}
|
||||
|
||||
TEST(libbacktrace, offline_max_trace) {
|
||||
std::function<int(void (*)(void*), void*)> function = std::bind(
|
||||
test_recursive_call, MAX_BACKTRACE_FRAMES + 10, std::placeholders::_1, std::placeholders::_2);
|
||||
std::vector<uintptr_t> pc_values;
|
||||
OfflineBacktraceFunctionCall(function, &pc_values);
|
||||
ASSERT_FALSE(pc_values.empty());
|
||||
ASSERT_EQ(static_cast<size_t>(MAX_BACKTRACE_FRAMES), pc_values.size());
|
||||
ASSERT_EQ("test_recursive_call", FunctionNameForAddress(pc_values.back()));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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>
|
||||
|
||||
int test_level_four(int one, int two, int three, int four,
|
||||
void (*callback_func)(void*), void* data) {
|
||||
if (callback_func != NULL) {
|
||||
callback_func(data);
|
||||
} else {
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
return one + two + three + four;
|
||||
}
|
||||
|
||||
int test_level_three(int one, int two, int three, int four,
|
||||
void (*callback_func)(void*), void* data) {
|
||||
return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3;
|
||||
}
|
||||
|
||||
int test_level_two(int one, int two, int three, int four,
|
||||
void (*callback_func)(void*), void* data) {
|
||||
return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2;
|
||||
}
|
||||
|
||||
int test_level_one(int one, int two, int three, int four,
|
||||
void (*callback_func)(void*), void* data) {
|
||||
return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1;
|
||||
}
|
||||
|
||||
int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
|
||||
if (level > 0) {
|
||||
return test_recursive_call(level - 1, callback_func, data) + level;
|
||||
} else if (callback_func != NULL) {
|
||||
callback_func(data);
|
||||
} else {
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 "thread_utils.h"
|
||||
|
||||
#if !defined(__BIONIC__)
|
||||
|
||||
// glibc doesn't implement or export tgkill.
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
int tgkill(int tgid, int tid, int sig) {
|
||||
return syscall(__NR_tgkill, tgid, tid, sig);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 _LIBBACKTRACE_THREAD_UTILS_H
|
||||
#define _LIBBACKTRACE_THREAD_UTILS_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#include <cutils/threads.h>
|
||||
#endif
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int tgkill(int tgid, int tid, int sig);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
|
||||
@@ -1,11 +0,0 @@
|
||||
BasedOnStyle: Google
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
|
||||
CommentPragmas: NOLINT:.*
|
||||
DerivePointerAlignment: false
|
||||
IndentWidth: 2
|
||||
PointerAlignment: Left
|
||||
TabWidth: 2
|
||||
UseTab: Never
|
||||
PenaltyExcessCharacter: 32
|
||||
@@ -1,59 +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.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
libbase_src_files := \
|
||||
file.cpp \
|
||||
logging.cpp \
|
||||
parsenetaddress.cpp \
|
||||
stringprintf.cpp \
|
||||
strings.cpp \
|
||||
test_utils.cpp \
|
||||
|
||||
libbase_cppflags := \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Werror \
|
||||
|
||||
libbase_linux_cppflags := \
|
||||
-Wexit-time-destructors \
|
||||
|
||||
libbase_darwin_cppflags := \
|
||||
-Wexit-time-destructors \
|
||||
|
||||
# Device
|
||||
# ------------------------------------------------------------------------------
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libcrashdumpbase_static
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SRC_FILES := $(libbase_src_files) $(libbase_linux_src_files)
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_CPPFLAGS := $(libbase_cppflags) $(libbase_linux_cppflags)
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
LOCAL_LDLIBS := -llog
|
||||
LOCAL_MULTILIB := both
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libcrashdumpbase
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := libcrashdumpbase_static
|
||||
LOCAL_LDLIBS := -llog
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
LOCAL_MULTILIB := both
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
set noparent
|
||||
filter=-build/header_guard,-build/include,-build/c++11,-whitespace/operators
|
||||
@@ -1,34 +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 "android-base/errors.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Error strings aren't consistent enough across systems to test the output,
|
||||
// just make sure we can compile correctly and nothing crashes even if we send
|
||||
// it possibly bogus error codes.
|
||||
TEST(ErrorsTest, TestSystemErrorString) {
|
||||
SystemErrorCodeToString(-1);
|
||||
SystemErrorCodeToString(0);
|
||||
SystemErrorCodeToString(1);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,29 +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 "android-base/errors.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
std::string SystemErrorCodeToString(int error_code) {
|
||||
return strerror(error_code);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,68 +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 "android-base/errors.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "android-base/strings.h"
|
||||
#include "android-base/utf8.h"
|
||||
|
||||
// A Windows error code is a DWORD. It's simpler to use an int error code for
|
||||
// both Unix and Windows if possible, but if this fails we'll need a different
|
||||
// function signature for each.
|
||||
static_assert(sizeof(int) >= sizeof(DWORD),
|
||||
"Windows system error codes are too large to fit in an int.");
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
static constexpr DWORD kErrorMessageBufferSize = 256;
|
||||
|
||||
std::string SystemErrorCodeToString(int int_error_code) {
|
||||
WCHAR msgbuf[kErrorMessageBufferSize];
|
||||
DWORD error_code = int_error_code;
|
||||
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||
DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
|
||||
kErrorMessageBufferSize, nullptr);
|
||||
if (len == 0) {
|
||||
return android::base::StringPrintf(
|
||||
"Error %lu while retrieving message for error %lu", GetLastError(),
|
||||
error_code);
|
||||
}
|
||||
|
||||
// Convert UTF-16 to UTF-8.
|
||||
std::string msg;
|
||||
if (!android::base::WideToUTF8(msgbuf, &msg)) {
|
||||
return android::base::StringPrintf(
|
||||
"Error %lu while converting message for error %lu from UTF-16 to UTF-8",
|
||||
GetLastError(), error_code);
|
||||
}
|
||||
|
||||
// Messages returned by the system end with line breaks.
|
||||
msg = android::base::Trim(msg);
|
||||
|
||||
// There are many Windows error messages compared to POSIX, so include the
|
||||
// numeric error code for easier, quicker, accurate identification. Use
|
||||
// decimal instead of hex because there are decimal ranges like 10000-11999
|
||||
// for Winsock.
|
||||
android::base::StringAppendF(&msg, " (%lu)", error_code);
|
||||
return msg;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,176 +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 "android-base/file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
|
||||
#include "android-base/utf8.h"
|
||||
#define LOG_TAG "base.file"
|
||||
#include "cutils/log.h"
|
||||
#include "utils/Compat.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Versions of standard library APIs that support UTF-8 strings.
|
||||
using namespace android::base::utf8;
|
||||
|
||||
bool ReadFdToString(int fd, std::string* content) {
|
||||
content->clear();
|
||||
|
||||
char buf[BUFSIZ];
|
||||
ssize_t n;
|
||||
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
|
||||
content->append(buf, n);
|
||||
}
|
||||
return (n == 0) ? true : false;
|
||||
}
|
||||
|
||||
bool ReadFileToString(const std::string& path, std::string* content) {
|
||||
content->clear();
|
||||
|
||||
int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
bool result = ReadFdToString(fd, content);
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WriteStringToFd(const std::string& content, int fd) {
|
||||
const char* p = content.data();
|
||||
size_t left = content.size();
|
||||
while (left > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left));
|
||||
if (n == -1) {
|
||||
return false;
|
||||
}
|
||||
p += n;
|
||||
left -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CleanUpAfterFailedWrite(const std::string& path) {
|
||||
// Something went wrong. Let's not leave a corrupt file lying around.
|
||||
int saved_errno = errno;
|
||||
unlink(path.c_str());
|
||||
errno = saved_errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
||||
mode_t mode, uid_t owner, gid_t group) {
|
||||
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
|
||||
int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
|
||||
if (fd == -1) {
|
||||
ALOGE("android::WriteStringToFile open failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// We do an explicit fchmod here because we assume that the caller really
|
||||
// meant what they said and doesn't want the umask-influenced mode.
|
||||
if (fchmod(fd, mode) == -1) {
|
||||
ALOGE("android::WriteStringToFile fchmod failed: %s", strerror(errno));
|
||||
return CleanUpAfterFailedWrite(path);
|
||||
}
|
||||
if (fchown(fd, owner, group) == -1) {
|
||||
ALOGE("android::WriteStringToFile fchown failed: %s", strerror(errno));
|
||||
return CleanUpAfterFailedWrite(path);
|
||||
}
|
||||
if (!WriteStringToFd(content, fd)) {
|
||||
ALOGE("android::WriteStringToFile write failed: %s", strerror(errno));
|
||||
return CleanUpAfterFailedWrite(path);
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WriteStringToFile(const std::string& content, const std::string& path) {
|
||||
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
|
||||
int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = WriteStringToFd(content, fd);
|
||||
close(fd);
|
||||
return result || CleanUpAfterFailedWrite(path);
|
||||
}
|
||||
|
||||
bool ReadFully(int fd, void* data, size_t byte_count) {
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(data);
|
||||
size_t remaining = byte_count;
|
||||
while (remaining > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
|
||||
if (n <= 0) return false;
|
||||
p += n;
|
||||
remaining -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFully(int fd, const void* data, size_t byte_count) {
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
|
||||
size_t remaining = byte_count;
|
||||
while (remaining > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
|
||||
if (n == -1) return false;
|
||||
p += n;
|
||||
remaining -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveFileIfExists(const std::string& path, std::string* err) {
|
||||
struct stat st;
|
||||
#if defined(_WIN32)
|
||||
//TODO: Windows version can't handle symbol link correctly.
|
||||
int result = stat(path.c_str(), &st);
|
||||
bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
|
||||
#else
|
||||
int result = lstat(path.c_str(), &st);
|
||||
bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
|
||||
#endif
|
||||
if (result == 0) {
|
||||
if (!file_type_removable) {
|
||||
if (err != nullptr) {
|
||||
*err = "is not a regular or symbol link file";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (unlink(path.c_str()) == -1) {
|
||||
if (err != nullptr) {
|
||||
*err = strerror(errno);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 "android-base/file.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "android-base/test_utils.h"
|
||||
|
||||
TEST(file, ReadFileToString_ENOENT) {
|
||||
std::string s("hello");
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ReadFileToString("/proc/does-not-exist", &s));
|
||||
EXPECT_EQ(ENOENT, errno);
|
||||
EXPECT_EQ("", s); // s was cleared.
|
||||
}
|
||||
|
||||
TEST(file, ReadFileToString_WriteStringToFile) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
|
||||
<< strerror(errno);
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
|
||||
<< strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
|
||||
// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
|
||||
// sense on Windows.
|
||||
#if !defined(_WIN32)
|
||||
TEST(file, WriteStringToFile2) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
|
||||
getuid(), getgid()))
|
||||
<< strerror(errno);
|
||||
struct stat sb;
|
||||
ASSERT_EQ(0, stat(tf.path, &sb));
|
||||
ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
|
||||
ASSERT_EQ(getuid(), sb.st_uid);
|
||||
ASSERT_EQ(getgid(), sb.st_gid);
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
|
||||
<< strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(file, WriteStringToFd) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
|
||||
TEST(file, WriteFully) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
std::string s;
|
||||
s.resize(3);
|
||||
ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
|
||||
<< strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
s.resize(1024);
|
||||
ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
|
||||
}
|
||||
|
||||
TEST(file, RemoveFileIfExist) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
close(tf.fd);
|
||||
tf.fd = -1;
|
||||
std::string err;
|
||||
ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err)) << err;
|
||||
ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path));
|
||||
TemporaryDir td;
|
||||
ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
|
||||
ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
|
||||
ASSERT_EQ("is not a regular or symbol link file", err);
|
||||
}
|
||||
@@ -1,46 +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.
|
||||
*/
|
||||
|
||||
// Portable error handling functions. This is only necessary for host-side
|
||||
// code that needs to be cross-platform; code that is only run on Unix should
|
||||
// just use errno and strerror() for simplicity.
|
||||
//
|
||||
// There is some complexity since Windows has (at least) three different error
|
||||
// numbers, not all of which share the same type:
|
||||
// * errno: for C runtime errors.
|
||||
// * GetLastError(): Windows non-socket errors.
|
||||
// * WSAGetLastError(): Windows socket errors.
|
||||
// errno can be passed to strerror() on all platforms, but the other two require
|
||||
// special handling to get the error string. Refer to Microsoft documentation
|
||||
// to determine which error code to check for each function.
|
||||
|
||||
#ifndef ANDROID_BASE_ERRORS_H
|
||||
#define ANDROID_BASE_ERRORS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Returns a string describing the given system error code. |error_code| must
|
||||
// be errno on Unix or GetLastError()/WSAGetLastError() on Windows. Passing
|
||||
// errno on Windows has undefined behavior.
|
||||
std::string SystemErrorCodeToString(int error_code);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_ERRORS_H
|
||||
@@ -1,49 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_FILE_H
|
||||
#define ANDROID_BASE_FILE_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <string>
|
||||
|
||||
#if !defined(_WIN32) && !defined(O_BINARY)
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
bool ReadFdToString(int fd, std::string* content);
|
||||
bool ReadFileToString(const std::string& path, std::string* content);
|
||||
|
||||
bool WriteStringToFile(const std::string& content, const std::string& path);
|
||||
bool WriteStringToFd(const std::string& content, int fd);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
||||
mode_t mode, uid_t owner, gid_t group);
|
||||
#endif
|
||||
|
||||
bool ReadFully(int fd, void* data, size_t byte_count);
|
||||
bool WriteFully(int fd, const void* data, size_t byte_count);
|
||||
|
||||
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_FILE_H
|
||||
@@ -1,338 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_LOGGING_H
|
||||
#define ANDROID_BASE_LOGGING_H
|
||||
|
||||
// NOTE: For Windows, you must include logging.h after windows.h to allow the
|
||||
// following code to suppress the evil ERROR macro:
|
||||
#ifdef _WIN32
|
||||
// windows.h includes wingdi.h which defines an evil macro ERROR.
|
||||
#ifdef ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
enum LogSeverity {
|
||||
VERBOSE,
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR,
|
||||
FATAL,
|
||||
};
|
||||
|
||||
enum LogId {
|
||||
DEFAULT,
|
||||
MAIN,
|
||||
SYSTEM,
|
||||
};
|
||||
|
||||
typedef std::function<void(LogId, LogSeverity, const char*, const char*,
|
||||
unsigned int, const char*)> LogFunction;
|
||||
|
||||
extern void StderrLogger(LogId, LogSeverity, const char*, const char*,
|
||||
unsigned int, const char*);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// We expose this even though it is the default because a user that wants to
|
||||
// override the default log buffer will have to construct this themselves.
|
||||
class LogdLogger {
|
||||
public:
|
||||
explicit LogdLogger(LogId default_log_id = android::base::MAIN);
|
||||
|
||||
void operator()(LogId, LogSeverity, const char* tag, const char* file,
|
||||
unsigned int line, const char* message);
|
||||
|
||||
private:
|
||||
LogId default_log_id_;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Configure logging based on ANDROID_LOG_TAGS environment variable.
|
||||
// We need to parse a string that looks like
|
||||
//
|
||||
// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
|
||||
//
|
||||
// The tag (or '*' for the global level) comes first, followed by a colon and a
|
||||
// letter indicating the minimum priority level we're expected to log. This can
|
||||
// be used to reveal or conceal logs with specific tags.
|
||||
extern void InitLogging(char* argv[], LogFunction&& logger);
|
||||
|
||||
// Configures logging using the default logger (logd for the device, stderr for
|
||||
// the host).
|
||||
extern void InitLogging(char* argv[]);
|
||||
|
||||
// Replace the current logger.
|
||||
extern void SetLogger(LogFunction&& logger);
|
||||
|
||||
// Get the minimum severity level for logging.
|
||||
extern LogSeverity GetMinimumLogSeverity();
|
||||
|
||||
class ErrnoRestorer {
|
||||
public:
|
||||
ErrnoRestorer()
|
||||
: saved_errno_(errno) {
|
||||
}
|
||||
|
||||
~ErrnoRestorer() {
|
||||
errno = saved_errno_;
|
||||
}
|
||||
|
||||
// Allow this object to be used as part of && operation.
|
||||
operator bool() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const int saved_errno_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
|
||||
};
|
||||
|
||||
// Logs a message to logcat on Android otherwise to stderr. If the severity is
|
||||
// FATAL it also causes an abort. For example:
|
||||
//
|
||||
// LOG(FATAL) << "We didn't expect to reach here";
|
||||
#define LOG(severity) LOG_TO(DEFAULT, severity)
|
||||
|
||||
// Logs a message to logcat with the specified log ID on Android otherwise to
|
||||
// stderr. If the severity is FATAL it also causes an abort.
|
||||
// Use an if-else statement instead of just an if statement here. So if there is a
|
||||
// else statement after LOG() macro, it won't bind to the if statement in the macro.
|
||||
// do-while(0) statement doesn't work here. Because we need to support << operator
|
||||
// following the macro, like "LOG(DEBUG) << xxx;".
|
||||
#define LOG_TO(dest, severity) \
|
||||
UNLIKELY(::android::base::severity >= ::android::base::GetMinimumLogSeverity()) && \
|
||||
::android::base::ErrnoRestorer() && \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, \
|
||||
::android::base::dest, \
|
||||
::android::base::severity, -1).stream()
|
||||
|
||||
// A variant of LOG that also logs the current errno value. To be used when
|
||||
// library calls fail.
|
||||
#define PLOG(severity) PLOG_TO(DEFAULT, severity)
|
||||
|
||||
// Behaves like PLOG, but logs to the specified log ID.
|
||||
#define PLOG_TO(dest, severity) \
|
||||
UNLIKELY(::android::base::severity >= ::android::base::GetMinimumLogSeverity()) && \
|
||||
::android::base::ErrnoRestorer() && \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, \
|
||||
::android::base::dest, \
|
||||
::android::base::severity, errno).stream()
|
||||
|
||||
// Marker that code is yet to be implemented.
|
||||
#define UNIMPLEMENTED(level) \
|
||||
LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
|
||||
|
||||
// Check whether condition x holds and LOG(FATAL) if not. The value of the
|
||||
// expression x is only evaluated once. Extra logging can be appended using <<
|
||||
// after. For example:
|
||||
//
|
||||
// CHECK(false == true) results in a log message of
|
||||
// "Check failed: false == true".
|
||||
#define CHECK(x) \
|
||||
LIKELY((x)) || \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
|
||||
::android::base::FATAL, -1).stream() \
|
||||
<< "Check failed: " #x << " "
|
||||
|
||||
// Helper for CHECK_xx(x,y) macros.
|
||||
#define CHECK_OP(LHS, RHS, OP) \
|
||||
for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
|
||||
UNLIKELY(!(_values.lhs OP _values.rhs)); \
|
||||
/* empty */) \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
|
||||
::android::base::FATAL, -1).stream() \
|
||||
<< "Check failed: " << #LHS << " " << #OP << " " << #RHS \
|
||||
<< " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
|
||||
|
||||
// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
|
||||
// of the expressions x and y is evaluated once. Extra logging can be appended
|
||||
// using << after. For example:
|
||||
//
|
||||
// CHECK_NE(0 == 1, false) results in
|
||||
// "Check failed: false != false (0==1=false, false=false) ".
|
||||
#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
|
||||
#define CHECK_NE(x, y) CHECK_OP(x, y, != )
|
||||
#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
|
||||
#define CHECK_LT(x, y) CHECK_OP(x, y, < )
|
||||
#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
|
||||
#define CHECK_GT(x, y) CHECK_OP(x, y, > )
|
||||
|
||||
// Helper for CHECK_STRxx(s1,s2) macros.
|
||||
#define CHECK_STROP(s1, s2, sense) \
|
||||
if (LIKELY((strcmp(s1, s2) == 0) == sense)) \
|
||||
; \
|
||||
else \
|
||||
LOG(FATAL) << "Check failed: " \
|
||||
<< "\"" << s1 << "\"" \
|
||||
<< (sense ? " == " : " != ") << "\"" << s2 << "\""
|
||||
|
||||
// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
|
||||
#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
|
||||
#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
|
||||
|
||||
// Perform the pthread function call(args), LOG(FATAL) on error.
|
||||
#define CHECK_PTHREAD_CALL(call, args, what) \
|
||||
do { \
|
||||
int rc = call args; \
|
||||
if (rc != 0) { \
|
||||
errno = rc; \
|
||||
PLOG(FATAL) << #call << " failed for " << what; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
// CHECK that can be used in a constexpr function. For example:
|
||||
//
|
||||
// constexpr int half(int n) {
|
||||
// return
|
||||
// DCHECK_CONSTEXPR(n >= 0, , 0)
|
||||
// CHECK_CONSTEXPR((n & 1) == 0),
|
||||
// << "Extra debugging output: n = " << n, 0)
|
||||
// n / 2;
|
||||
// }
|
||||
#define CHECK_CONSTEXPR(x, out, dummy) \
|
||||
(UNLIKELY(!(x))) \
|
||||
? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
|
||||
:
|
||||
|
||||
// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
|
||||
// CHECK should be used unless profiling identifies a CHECK as being in
|
||||
// performance critical code.
|
||||
#if defined(NDEBUG)
|
||||
static constexpr bool kEnableDChecks = false;
|
||||
#else
|
||||
static constexpr bool kEnableDChecks = true;
|
||||
#endif
|
||||
|
||||
#define DCHECK(x) \
|
||||
if (::android::base::kEnableDChecks) CHECK(x)
|
||||
#define DCHECK_EQ(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
|
||||
#define DCHECK_NE(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_NE(x, y)
|
||||
#define DCHECK_LE(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_LE(x, y)
|
||||
#define DCHECK_LT(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_LT(x, y)
|
||||
#define DCHECK_GE(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_GE(x, y)
|
||||
#define DCHECK_GT(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_GT(x, y)
|
||||
#define DCHECK_STREQ(s1, s2) \
|
||||
if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
|
||||
#define DCHECK_STRNE(s1, s2) \
|
||||
if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
|
||||
#if defined(NDEBUG)
|
||||
#define DCHECK_CONSTEXPR(x, out, dummy)
|
||||
#else
|
||||
#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
|
||||
#endif
|
||||
|
||||
// Temporary class created to evaluate the LHS and RHS, used with
|
||||
// MakeEagerEvaluator to infer the types of LHS and RHS.
|
||||
template <typename LHS, typename RHS>
|
||||
struct EagerEvaluator {
|
||||
EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
|
||||
}
|
||||
LHS lhs;
|
||||
RHS rhs;
|
||||
};
|
||||
|
||||
// Helper function for CHECK_xx.
|
||||
template <typename LHS, typename RHS>
|
||||
static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
|
||||
return EagerEvaluator<LHS, RHS>(lhs, rhs);
|
||||
}
|
||||
|
||||
// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
|
||||
// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
|
||||
// signed/unsigned warnings to protect you against combinations not explicitly
|
||||
// listed below.
|
||||
#define EAGER_PTR_EVALUATOR(T1, T2) \
|
||||
template <> \
|
||||
struct EagerEvaluator<T1, T2> { \
|
||||
EagerEvaluator(T1 l, T2 r) \
|
||||
: lhs(reinterpret_cast<const void*>(l)), \
|
||||
rhs(reinterpret_cast<const void*>(r)) { \
|
||||
} \
|
||||
const void* lhs; \
|
||||
const void* rhs; \
|
||||
}
|
||||
EAGER_PTR_EVALUATOR(const char*, const char*);
|
||||
EAGER_PTR_EVALUATOR(const char*, char*);
|
||||
EAGER_PTR_EVALUATOR(char*, const char*);
|
||||
EAGER_PTR_EVALUATOR(char*, char*);
|
||||
EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
|
||||
EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
|
||||
EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
|
||||
EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
|
||||
EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
|
||||
EAGER_PTR_EVALUATOR(const signed char*, signed char*);
|
||||
EAGER_PTR_EVALUATOR(signed char*, const signed char*);
|
||||
EAGER_PTR_EVALUATOR(signed char*, signed char*);
|
||||
|
||||
// Data for the log message, not stored in LogMessage to avoid increasing the
|
||||
// stack size.
|
||||
class LogMessageData;
|
||||
|
||||
// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
|
||||
// of a CHECK. The destructor will abort if the severity is FATAL.
|
||||
class LogMessage {
|
||||
public:
|
||||
LogMessage(const char* file, unsigned int line, LogId id,
|
||||
LogSeverity severity, int error);
|
||||
|
||||
~LogMessage();
|
||||
|
||||
// Returns the stream associated with the message, the LogMessage performs
|
||||
// output when it goes out of scope.
|
||||
std::ostream& stream();
|
||||
|
||||
// The routine that performs the actual logging.
|
||||
static void LogLine(const char* file, unsigned int line, LogId id,
|
||||
LogSeverity severity, const char* msg);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<LogMessageData> data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LogMessage);
|
||||
};
|
||||
|
||||
// Allows to temporarily change the minimum severity level for logging.
|
||||
class ScopedLogSeverity {
|
||||
public:
|
||||
explicit ScopedLogSeverity(LogSeverity level);
|
||||
~ScopedLogSeverity();
|
||||
|
||||
private:
|
||||
LogSeverity old_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_LOGGING_H
|
||||
@@ -1,188 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_MACROS_H
|
||||
#define ANDROID_BASE_MACROS_H
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <unistd.h> // for TEMP_FAILURE_RETRY
|
||||
|
||||
// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
|
||||
#ifndef TEMP_FAILURE_RETRY
|
||||
#define TEMP_FAILURE_RETRY(exp) \
|
||||
({ \
|
||||
decltype(exp) _rc; \
|
||||
do { \
|
||||
_rc = (exp); \
|
||||
} while (_rc == -1 && errno == EINTR); \
|
||||
_rc; \
|
||||
})
|
||||
#endif
|
||||
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This must be placed in the private: declarations for a class.
|
||||
//
|
||||
// For disallowing only assign or copy, delete the relevant operator or
|
||||
// constructor, for example:
|
||||
// void operator=(const TypeName&) = delete;
|
||||
// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
|
||||
// semantically, one should either use disallow both or neither. Try to
|
||||
// avoid these in new code.
|
||||
//
|
||||
// When building with C++11 toolchains, just use the language support
|
||||
// for explicitly deleted methods.
|
||||
#if __cplusplus >= 201103L
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
void operator=(const TypeName&) = delete
|
||||
#else
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
#endif
|
||||
|
||||
// A macro to disallow all the implicit constructors, namely the
|
||||
// default constructor, copy constructor and operator= functions.
|
||||
//
|
||||
// This should be used in the private: declarations for a class
|
||||
// that wants to prevent anyone from instantiating it. This is
|
||||
// especially useful for classes containing only static methods.
|
||||
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName(); \
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
|
||||
// The arraysize(arr) macro returns the # of elements in an array arr.
|
||||
// The expression is a compile-time constant, and therefore can be
|
||||
// used in defining new arrays, for example. If you use arraysize on
|
||||
// a pointer by mistake, you will get a compile-time error.
|
||||
//
|
||||
// One caveat is that arraysize() doesn't accept any array of an
|
||||
// anonymous type or a type defined inside a function. In these rare
|
||||
// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
|
||||
// due to a limitation in C++'s template system. The limitation might
|
||||
// eventually be removed, but it hasn't happened yet.
|
||||
|
||||
// This template function declaration is used in defining arraysize.
|
||||
// Note that the function doesn't need an implementation, as we only
|
||||
// use its type.
|
||||
template <typename T, size_t N>
|
||||
char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
|
||||
|
||||
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
|
||||
|
||||
// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
|
||||
// but can be used on anonymous types or types defined inside
|
||||
// functions. It's less safe than arraysize as it accepts some
|
||||
// (although not all) pointers. Therefore, you should use arraysize
|
||||
// whenever possible.
|
||||
//
|
||||
// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
|
||||
// size_t.
|
||||
//
|
||||
// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
|
||||
//
|
||||
// "warning: division by zero in ..."
|
||||
//
|
||||
// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
|
||||
// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
|
||||
//
|
||||
// The following comments are on the implementation details, and can
|
||||
// be ignored by the users.
|
||||
//
|
||||
// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
|
||||
// the array) and sizeof(*(arr)) (the # of bytes in one array
|
||||
// element). If the former is divisible by the latter, perhaps arr is
|
||||
// indeed an array, in which case the division result is the # of
|
||||
// elements in the array. Otherwise, arr cannot possibly be an array,
|
||||
// and we generate a compiler error to prevent the code from
|
||||
// compiling.
|
||||
//
|
||||
// Since the size of bool is implementation-defined, we need to cast
|
||||
// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
|
||||
// result has type size_t.
|
||||
//
|
||||
// This macro is not perfect as it wrongfully accepts certain
|
||||
// pointers, namely where the pointer size is divisible by the pointee
|
||||
// size. Since all our code has to go through a 32-bit compiler,
|
||||
// where a pointer is 4 bytes, this means all pointers to a type whose
|
||||
// size is 3 or greater than 4 will be (righteously) rejected.
|
||||
#define ARRAYSIZE_UNSAFE(a) \
|
||||
((sizeof(a) / sizeof(*(a))) / \
|
||||
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
|
||||
|
||||
#define LIKELY(x) __builtin_expect((x), true)
|
||||
#define UNLIKELY(x) __builtin_expect((x), false)
|
||||
|
||||
#define WARN_UNUSED __attribute__((warn_unused_result))
|
||||
|
||||
// A deprecated function to call to create a false use of the parameter, for
|
||||
// example:
|
||||
// int foo(int x) { UNUSED(x); return 10; }
|
||||
// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
|
||||
template <typename... T>
|
||||
void UNUSED(const T&...) {
|
||||
}
|
||||
|
||||
// An attribute to place on a parameter to a function, for example:
|
||||
// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
|
||||
// to avoid compiler warnings.
|
||||
#define ATTRIBUTE_UNUSED __attribute__((__unused__))
|
||||
|
||||
// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
|
||||
// between switch labels:
|
||||
// switch (x) {
|
||||
// case 40:
|
||||
// case 41:
|
||||
// if (truth_is_out_there) {
|
||||
// ++x;
|
||||
// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
|
||||
// // comments.
|
||||
// } else {
|
||||
// return x;
|
||||
// }
|
||||
// case 42:
|
||||
// ...
|
||||
//
|
||||
// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
|
||||
// followed by a semicolon. It is designed to mimic control-flow statements
|
||||
// like 'break;', so it can be placed in most places where 'break;' can, but
|
||||
// only if there are no statements on the execution path between it and the
|
||||
// next switch label.
|
||||
//
|
||||
// When compiled with clang in C++11 mode, the FALLTHROUGH_INTENDED macro is
|
||||
// expanded to [[clang::fallthrough]] attribute, which is analysed when
|
||||
// performing switch labels fall-through diagnostic ('-Wimplicit-fallthrough').
|
||||
// See clang documentation on language extensions for details:
|
||||
// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
|
||||
//
|
||||
// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
|
||||
// effect on diagnostics.
|
||||
//
|
||||
// In either case this macro has no effect on runtime behavior and performance
|
||||
// of code.
|
||||
#if defined(__clang__) && __cplusplus >= 201103L && defined(__has_warning)
|
||||
#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
|
||||
#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FALLTHROUGH_INTENDED
|
||||
#define FALLTHROUGH_INTENDED \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif // ANDROID_BASE_MACROS_H
|
||||
@@ -1,47 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_MEMORY_H
|
||||
#define ANDROID_BASE_MEMORY_H
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Use packed structures for access to unaligned data on targets with alignment
|
||||
// restrictions. The compiler will generate appropriate code to access these
|
||||
// structures without generating alignment exceptions.
|
||||
template <typename T>
|
||||
static inline T get_unaligned(const T* address) {
|
||||
struct unaligned {
|
||||
T v;
|
||||
} __attribute__((packed));
|
||||
const unaligned* p = reinterpret_cast<const unaligned*>(address);
|
||||
return p->v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void put_unaligned(T* address, T v) {
|
||||
struct unaligned {
|
||||
T v;
|
||||
} __attribute__((packed));
|
||||
unaligned* p = reinterpret_cast<unaligned*>(address);
|
||||
p->v = v;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_MEMORY_H
|
||||
@@ -1,73 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_PARSEINT_H
|
||||
#define ANDROID_BASE_PARSEINT_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Parses the unsigned decimal integer in the string 's' and sets 'out' to
|
||||
// that value. Optionally allows the caller to define a 'max' beyond which
|
||||
// otherwise valid values will be rejected. Returns boolean success.
|
||||
template <typename T>
|
||||
bool ParseUint(const char* s, T* out,
|
||||
T max = std::numeric_limits<T>::max()) {
|
||||
int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
|
||||
errno = 0;
|
||||
char* end;
|
||||
unsigned long long int result = strtoull(s, &end, base);
|
||||
if (errno != 0 || s == end || *end != '\0') {
|
||||
return false;
|
||||
}
|
||||
if (max < result) {
|
||||
return false;
|
||||
}
|
||||
*out = static_cast<T>(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses the signed decimal integer in the string 's' and sets 'out' to
|
||||
// that value. Optionally allows the caller to define a 'min' and 'max
|
||||
// beyond which otherwise valid values will be rejected. Returns boolean
|
||||
// success.
|
||||
template <typename T>
|
||||
bool ParseInt(const char* s, T* out,
|
||||
T min = std::numeric_limits<T>::min(),
|
||||
T max = std::numeric_limits<T>::max()) {
|
||||
int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
|
||||
errno = 0;
|
||||
char* end;
|
||||
long long int result = strtoll(s, &end, base);
|
||||
if (errno != 0 || s == end || *end != '\0') {
|
||||
return false;
|
||||
}
|
||||
if (result < min || max < result) {
|
||||
return false;
|
||||
}
|
||||
*out = static_cast<T>(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_PARSEINT_H
|
||||
@@ -1,38 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_PARSENETADDRESS_H
|
||||
#define ANDROID_BASE_PARSENETADDRESS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Parses |address| into |host| and |port|.
|
||||
//
|
||||
// If |address| doesn't contain a port number, the default value is taken from
|
||||
// |port|. If |canonical_address| is non-null it will be set to "host:port" or
|
||||
// "[host]:port" as appropriate.
|
||||
//
|
||||
// On failure, returns false and fills |error|.
|
||||
bool ParseNetAddress(const std::string& address, std::string* host, int* port,
|
||||
std::string* canonical_address, std::string* error);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_PARSENETADDRESS_H
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 ANDROID_BASE_STRINGPRINTF_H
|
||||
#define ANDROID_BASE_STRINGPRINTF_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// These printf-like functions are implemented in terms of vsnprintf, so they
|
||||
// use the same attribute for compile-time format string checking. On Windows,
|
||||
// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
|
||||
// in %zd and PRIu64 (and related) to be recognized by the compile-time
|
||||
// checking.
|
||||
#define FORMAT_ARCHETYPE __printf__
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
#if __USE_MINGW_ANSI_STDIO
|
||||
#undef FORMAT_ARCHETYPE
|
||||
#define FORMAT_ARCHETYPE gnu_printf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Returns a string corresponding to printf-like formatting of the arguments.
|
||||
std::string StringPrintf(const char* fmt, ...)
|
||||
__attribute__((__format__(FORMAT_ARCHETYPE, 1, 2)));
|
||||
|
||||
// Appends a printf-like formatting of the arguments to 'dst'.
|
||||
void StringAppendF(std::string* dst, const char* fmt, ...)
|
||||
__attribute__((__format__(FORMAT_ARCHETYPE, 2, 3)));
|
||||
|
||||
// Appends a printf-like formatting of the arguments to 'dst'.
|
||||
void StringAppendV(std::string* dst, const char* format, va_list ap)
|
||||
__attribute__((__format__(FORMAT_ARCHETYPE, 2, 0)));
|
||||
|
||||
#undef FORMAT_ARCHETYPE
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_STRINGPRINTF_H
|
||||
@@ -1,68 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_STRINGS_H
|
||||
#define ANDROID_BASE_STRINGS_H
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Splits a string into a vector of strings.
|
||||
//
|
||||
// The string is split at each occurrence of a character in delimiters.
|
||||
//
|
||||
// The empty string is not a valid delimiter list.
|
||||
std::vector<std::string> Split(const std::string& s,
|
||||
const std::string& delimiters);
|
||||
|
||||
// Trims whitespace off both ends of the given string.
|
||||
std::string Trim(const std::string& s);
|
||||
|
||||
// Joins a container of things into a single string, using the given separator.
|
||||
template <typename ContainerT, typename SeparatorT>
|
||||
std::string Join(const ContainerT& things, SeparatorT separator) {
|
||||
if (things.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostringstream result;
|
||||
result << *things.begin();
|
||||
for (auto it = std::next(things.begin()); it != things.end(); ++it) {
|
||||
result << separator << *it;
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
// We instantiate the common cases in strings.cpp.
|
||||
extern template std::string Join(const std::vector<std::string>&, char);
|
||||
extern template std::string Join(const std::vector<const char*>&, char);
|
||||
extern template std::string Join(const std::vector<std::string>&, const std::string&);
|
||||
extern template std::string Join(const std::vector<const char*>&, const std::string&);
|
||||
|
||||
// Tests whether 's' starts with 'prefix'.
|
||||
bool StartsWith(const std::string& s, const char* prefix);
|
||||
|
||||
// Tests whether 's' ends with 'suffix'.
|
||||
bool EndsWith(const std::string& s, const char* suffix);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_STRINGS_H
|
||||
@@ -1,51 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_TEST_UTILS_H
|
||||
#define ANDROID_BASE_TEST_UTILS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
|
||||
class TemporaryFile {
|
||||
public:
|
||||
TemporaryFile();
|
||||
~TemporaryFile();
|
||||
|
||||
int fd;
|
||||
char path[1024];
|
||||
|
||||
private:
|
||||
void init(const std::string& tmp_dir);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
|
||||
};
|
||||
|
||||
class TemporaryDir {
|
||||
public:
|
||||
TemporaryDir();
|
||||
~TemporaryDir();
|
||||
|
||||
char path[1024];
|
||||
|
||||
private:
|
||||
bool init(const std::string& tmp_dir);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
|
||||
};
|
||||
|
||||
#endif // ANDROID_BASE_TEST_UTILS_H
|
||||
@@ -1,83 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
|
||||
#define ANDROID_BASE_THREAD_ANNOTATIONS_H
|
||||
|
||||
#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||
#else
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||
#endif
|
||||
|
||||
#define CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
|
||||
|
||||
#define SCOPED_CAPABILITY \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||
|
||||
#define GUARDED_BY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||
|
||||
#define PT_GUARDED_BY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
||||
|
||||
#define ACQUIRED_BEFORE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRED_AFTER(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define EXCLUDES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
||||
|
||||
#define ASSERT_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
|
||||
|
||||
#define ASSERT_SHARED_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
|
||||
|
||||
#define RETURN_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||
|
||||
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||
|
||||
#endif // ANDROID_BASE_THREAD_ANNOTATIONS_H
|
||||
@@ -1,98 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_UNIQUE_FD_H
|
||||
#define ANDROID_BASE_UNIQUE_FD_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
// DO NOT INCLUDE OTHER LIBBASE HEADERS!
|
||||
// This file gets used in libbinder, and libbinder is used everywhere.
|
||||
// Including other headers from libbase frequently results in inclusion of
|
||||
// android-base/macros.h, which causes macro collisions.
|
||||
|
||||
// Container for a file descriptor that automatically closes the descriptor as
|
||||
// it goes out of scope.
|
||||
//
|
||||
// unique_fd ufd(open("/some/path", "r"));
|
||||
// if (ufd.get() == -1) return error;
|
||||
//
|
||||
// // Do something useful, possibly including 'return'.
|
||||
//
|
||||
// return 0; // Descriptor is closed for you.
|
||||
//
|
||||
// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
|
||||
// you find this class if you're searching for one of those names.
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
struct DefaultCloser {
|
||||
static void Close(int fd) {
|
||||
// Even if close(2) fails with EINTR, the fd will have been closed.
|
||||
// Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
|
||||
// else's fd.
|
||||
// http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
|
||||
::close(fd);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Closer>
|
||||
class unique_fd_impl final {
|
||||
public:
|
||||
unique_fd_impl() : value_(-1) {}
|
||||
|
||||
explicit unique_fd_impl(int value) : value_(value) {}
|
||||
~unique_fd_impl() { clear(); }
|
||||
|
||||
unique_fd_impl(unique_fd_impl&& other) : value_(other.release()) {}
|
||||
unique_fd_impl& operator=(unique_fd_impl&& s) {
|
||||
reset(s.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset(int new_value) {
|
||||
if (value_ != -1) {
|
||||
Closer::Close(value_);
|
||||
}
|
||||
value_ = new_value;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
reset(-1);
|
||||
}
|
||||
|
||||
int get() const { return value_; }
|
||||
operator int() const { return get(); }
|
||||
|
||||
int release() __attribute__((warn_unused_result)) {
|
||||
int ret = value_;
|
||||
value_ = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
int value_;
|
||||
|
||||
unique_fd_impl(const unique_fd_impl&);
|
||||
void operator=(const unique_fd_impl&);
|
||||
};
|
||||
|
||||
using unique_fd = unique_fd_impl<DefaultCloser>;
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_UNIQUE_FD_H
|
||||
@@ -1,87 +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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_BASE_UTF8_H
|
||||
#define ANDROID_BASE_UTF8_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <string>
|
||||
#else
|
||||
// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
|
||||
#include <fcntl.h> // open
|
||||
#include <unistd.h> // unlink
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Only available on Windows because this is only needed on Windows.
|
||||
#ifdef _WIN32
|
||||
// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
|
||||
// conversion was done successfully.
|
||||
bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
|
||||
|
||||
// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
|
||||
// whether the conversion was done successfully.
|
||||
bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
|
||||
|
||||
// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
|
||||
// UTF-8. Returns whether the conversion was done successfully.
|
||||
bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
|
||||
|
||||
// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
|
||||
// was done successfully.
|
||||
bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
|
||||
|
||||
// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
|
||||
// whether the conversion was done successfully.
|
||||
bool UTF8ToWide(const char* utf8, std::wstring* utf16);
|
||||
|
||||
// Convert a UTF-8 std::string (including any embedded NULL characters) to
|
||||
// UTF-16. Returns whether the conversion was done successfully.
|
||||
bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
|
||||
#endif
|
||||
|
||||
// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
|
||||
// are wrappers, for non-Windows these just expose existing APIs. To call these
|
||||
// functions, use:
|
||||
//
|
||||
// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
|
||||
// namespace {
|
||||
// // Import functions into anonymous namespace.
|
||||
// using namespace android::base::utf8;
|
||||
//
|
||||
// void SomeFunction(const char* name) {
|
||||
// int fd = open(name, ...); // Calls android::base::utf8::open().
|
||||
// ...
|
||||
// unlink(name); // Calls android::base::utf8::unlink().
|
||||
// }
|
||||
// }
|
||||
namespace utf8 {
|
||||
|
||||
#ifdef _WIN32
|
||||
int open(const char* name, int flags, ...);
|
||||
int unlink(const char* name);
|
||||
#else
|
||||
using ::open;
|
||||
using ::unlink;
|
||||
#endif
|
||||
|
||||
} // namespace utf8
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_BASE_UTF8_H
|
||||
@@ -1,428 +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.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "android-base/logging.h"
|
||||
|
||||
#include <libgen.h>
|
||||
|
||||
// For getprogname(3) or program_invocation_short_name.
|
||||
#if defined(__ANDROID__) || defined(__APPLE__)
|
||||
#include <stdlib.h>
|
||||
#elif defined(__GLIBC__)
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <mutex>
|
||||
#endif
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "android-base/strings.h"
|
||||
#include "cutils/threads.h"
|
||||
|
||||
// Headers for LogMessage::LogLine.
|
||||
#ifdef __ANDROID__
|
||||
#include <android/set_abort_message.h>
|
||||
#include "cutils/log.h"
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// For gettid.
|
||||
#if defined(__APPLE__)
|
||||
#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
typedef uint32_t thread_id;
|
||||
#else
|
||||
typedef pid_t thread_id;
|
||||
#endif
|
||||
|
||||
static thread_id GetThreadId() {
|
||||
#if defined(__BIONIC__)
|
||||
return gettid();
|
||||
#elif defined(__APPLE__)
|
||||
return syscall(SYS_thread_selfid);
|
||||
#elif defined(__linux__)
|
||||
return syscall(__NR_gettid);
|
||||
#elif defined(_WIN32)
|
||||
return GetCurrentThreadId();
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
#ifndef _WIN32
|
||||
using std::mutex;
|
||||
using std::lock_guard;
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
const char* getprogname() {
|
||||
return program_invocation_short_name;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
const char* getprogname() {
|
||||
static bool first = true;
|
||||
static char progname[MAX_PATH] = {};
|
||||
|
||||
if (first) {
|
||||
CHAR longname[MAX_PATH];
|
||||
DWORD nchars = GetModuleFileNameA(nullptr, longname, arraysize(longname));
|
||||
if ((nchars >= arraysize(longname)) || (nchars == 0)) {
|
||||
// String truncation or some other error.
|
||||
strcpy(progname, "<unknown>");
|
||||
} else {
|
||||
strcpy(progname, basename(longname));
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
return progname;
|
||||
}
|
||||
|
||||
class mutex {
|
||||
public:
|
||||
mutex() {
|
||||
InitializeCriticalSection(&critical_section_);
|
||||
}
|
||||
~mutex() {
|
||||
DeleteCriticalSection(&critical_section_);
|
||||
}
|
||||
|
||||
void lock() {
|
||||
EnterCriticalSection(&critical_section_);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
LeaveCriticalSection(&critical_section_);
|
||||
}
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION critical_section_;
|
||||
};
|
||||
|
||||
template <typename LockT>
|
||||
class lock_guard {
|
||||
public:
|
||||
explicit lock_guard(LockT& lock) : lock_(lock) {
|
||||
lock_.lock();
|
||||
}
|
||||
|
||||
~lock_guard() {
|
||||
lock_.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
LockT& lock_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(lock_guard);
|
||||
};
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
static auto& logging_lock = *new mutex();
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static auto& gLogger = *new LogFunction(LogdLogger());
|
||||
#else
|
||||
static auto& gLogger = *new LogFunction(StderrLogger);
|
||||
#endif
|
||||
|
||||
static bool gInitialized = false;
|
||||
static LogSeverity gMinimumLogSeverity = INFO;
|
||||
static auto& gProgramInvocationName = *new std::unique_ptr<std::string>();
|
||||
|
||||
LogSeverity GetMinimumLogSeverity() {
|
||||
return gMinimumLogSeverity;
|
||||
}
|
||||
|
||||
static const char* ProgramInvocationName() {
|
||||
if (gProgramInvocationName == nullptr) {
|
||||
gProgramInvocationName.reset(new std::string(getprogname()));
|
||||
}
|
||||
|
||||
return gProgramInvocationName->c_str();
|
||||
}
|
||||
|
||||
void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
|
||||
unsigned int line, const char* message) {
|
||||
static const char log_characters[] = "VDIWEF";
|
||||
static_assert(arraysize(log_characters) - 1 == FATAL + 1,
|
||||
"Mismatch in size of log_characters and values in LogSeverity");
|
||||
char severity_char = log_characters[severity];
|
||||
fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
|
||||
severity_char, getpid(), GetThreadId(), file, line, message);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __ANDROID__
|
||||
LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
|
||||
}
|
||||
|
||||
static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
|
||||
ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
|
||||
ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
|
||||
};
|
||||
static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
|
||||
"Mismatch in size of kLogSeverityToAndroidLogPriority and values "
|
||||
"in LogSeverity");
|
||||
|
||||
static const log_id kLogIdToAndroidLogId[] = {
|
||||
LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM,
|
||||
};
|
||||
static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
|
||||
"Mismatch in size of kLogIdToAndroidLogId and values in LogId");
|
||||
|
||||
void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
|
||||
const char* file, unsigned int line,
|
||||
const char* message) {
|
||||
int priority = kLogSeverityToAndroidLogPriority[severity];
|
||||
if (id == DEFAULT) {
|
||||
id = default_log_id_;
|
||||
}
|
||||
|
||||
log_id lg_id = kLogIdToAndroidLogId[id];
|
||||
|
||||
if (priority == ANDROID_LOG_FATAL) {
|
||||
__android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line,
|
||||
message);
|
||||
} else {
|
||||
__android_log_buf_print(lg_id, priority, tag, "%s", message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void InitLogging(char* argv[], LogFunction&& logger) {
|
||||
SetLogger(std::forward<LogFunction>(logger));
|
||||
InitLogging(argv);
|
||||
}
|
||||
|
||||
void InitLogging(char* argv[]) {
|
||||
if (gInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
gInitialized = true;
|
||||
|
||||
// Stash the command line for later use. We can use /proc/self/cmdline on
|
||||
// Linux to recover this, but we don't have that luxury on the Mac/Windows,
|
||||
// and there are a couple of argv[0] variants that are commonly used.
|
||||
if (argv != nullptr) {
|
||||
gProgramInvocationName.reset(new std::string(basename(argv[0])));
|
||||
}
|
||||
|
||||
const char* tags = getenv("ANDROID_LOG_TAGS");
|
||||
if (tags == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> specs = Split(tags, " ");
|
||||
for (size_t i = 0; i < specs.size(); ++i) {
|
||||
// "tag-pattern:[vdiwefs]"
|
||||
std::string spec(specs[i]);
|
||||
if (spec.size() == 3 && StartsWith(spec, "*:")) {
|
||||
switch (spec[2]) {
|
||||
case 'v':
|
||||
gMinimumLogSeverity = VERBOSE;
|
||||
continue;
|
||||
case 'd':
|
||||
gMinimumLogSeverity = DEBUG;
|
||||
continue;
|
||||
case 'i':
|
||||
gMinimumLogSeverity = INFO;
|
||||
continue;
|
||||
case 'w':
|
||||
gMinimumLogSeverity = WARNING;
|
||||
continue;
|
||||
case 'e':
|
||||
gMinimumLogSeverity = ERROR;
|
||||
continue;
|
||||
case 'f':
|
||||
gMinimumLogSeverity = FATAL;
|
||||
continue;
|
||||
// liblog will even suppress FATAL if you say 's' for silent, but that's
|
||||
// crazy!
|
||||
case 's':
|
||||
gMinimumLogSeverity = FATAL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
|
||||
<< ")";
|
||||
}
|
||||
}
|
||||
|
||||
void SetLogger(LogFunction&& logger) {
|
||||
lock_guard<mutex> lock(logging_lock);
|
||||
gLogger = std::move(logger);
|
||||
}
|
||||
|
||||
static const char* GetFileBasename(const char* file) {
|
||||
// We can't use basename(3) even on Unix because the Mac doesn't
|
||||
// have a non-modifying basename.
|
||||
const char* last_slash = strrchr(file, '/');
|
||||
if (last_slash != nullptr) {
|
||||
return last_slash + 1;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
const char* last_backslash = strrchr(file, '\\');
|
||||
if (last_backslash != nullptr) {
|
||||
return last_backslash + 1;
|
||||
}
|
||||
#endif
|
||||
return file;
|
||||
}
|
||||
|
||||
// This indirection greatly reduces the stack impact of having lots of
|
||||
// checks/logging in a function.
|
||||
class LogMessageData {
|
||||
public:
|
||||
LogMessageData(const char* file, unsigned int line, LogId id,
|
||||
LogSeverity severity, int error)
|
||||
: file_(GetFileBasename(file)),
|
||||
line_number_(line),
|
||||
id_(id),
|
||||
severity_(severity),
|
||||
error_(error) {
|
||||
}
|
||||
|
||||
const char* GetFile() const {
|
||||
return file_;
|
||||
}
|
||||
|
||||
unsigned int GetLineNumber() const {
|
||||
return line_number_;
|
||||
}
|
||||
|
||||
LogSeverity GetSeverity() const {
|
||||
return severity_;
|
||||
}
|
||||
|
||||
LogId GetId() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
int GetError() const {
|
||||
return error_;
|
||||
}
|
||||
|
||||
std::ostream& GetBuffer() {
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
std::string ToString() const {
|
||||
return buffer_.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream buffer_;
|
||||
const char* const file_;
|
||||
const unsigned int line_number_;
|
||||
const LogId id_;
|
||||
const LogSeverity severity_;
|
||||
const int error_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LogMessageData);
|
||||
};
|
||||
|
||||
LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
|
||||
LogSeverity severity, int error)
|
||||
: data_(new LogMessageData(file, line, id, severity, error)) {
|
||||
}
|
||||
|
||||
LogMessage::~LogMessage() {
|
||||
// Finish constructing the message.
|
||||
if (data_->GetError() != -1) {
|
||||
data_->GetBuffer() << ": " << strerror(data_->GetError());
|
||||
}
|
||||
std::string msg(data_->ToString());
|
||||
|
||||
{
|
||||
// Do the actual logging with the lock held.
|
||||
lock_guard<mutex> lock(logging_lock);
|
||||
if (msg.find('\n') == std::string::npos) {
|
||||
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
|
||||
data_->GetSeverity(), msg.c_str());
|
||||
} else {
|
||||
msg += '\n';
|
||||
size_t i = 0;
|
||||
while (i < msg.size()) {
|
||||
size_t nl = msg.find('\n', i);
|
||||
msg[nl] = '\0';
|
||||
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
|
||||
data_->GetSeverity(), &msg[i]);
|
||||
i = nl + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Abort if necessary.
|
||||
if (data_->GetSeverity() == FATAL) {
|
||||
#ifdef __ANDROID__
|
||||
android_set_abort_message(msg.c_str());
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& LogMessage::stream() {
|
||||
return data_->GetBuffer();
|
||||
}
|
||||
|
||||
void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
|
||||
LogSeverity severity, const char* message) {
|
||||
const char* tag = ProgramInvocationName();
|
||||
gLogger(id, severity, tag, file, line, message);
|
||||
}
|
||||
|
||||
ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
|
||||
old_ = gMinimumLogSeverity;
|
||||
gMinimumLogSeverity = level;
|
||||
}
|
||||
|
||||
ScopedLogSeverity::~ScopedLogSeverity() {
|
||||
gMinimumLogSeverity = old_;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,281 +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 "android-base/logging.h"
|
||||
|
||||
#include <libgen.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "android-base/file.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "android-base/test_utils.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name)
|
||||
#else
|
||||
#define HOST_TEST(suite, name) TEST(suite, name)
|
||||
#endif
|
||||
|
||||
class CapturedStderr {
|
||||
public:
|
||||
CapturedStderr() : old_stderr_(-1) {
|
||||
init();
|
||||
}
|
||||
|
||||
~CapturedStderr() {
|
||||
reset();
|
||||
}
|
||||
|
||||
int fd() const {
|
||||
return temp_file_.fd;
|
||||
}
|
||||
|
||||
private:
|
||||
void init() {
|
||||
#if defined(_WIN32)
|
||||
// On Windows, stderr is often buffered, so make sure it is unbuffered so
|
||||
// that we can immediately read back what was written to stderr.
|
||||
ASSERT_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
|
||||
#endif
|
||||
old_stderr_ = dup(STDERR_FILENO);
|
||||
ASSERT_NE(-1, old_stderr_);
|
||||
ASSERT_NE(-1, dup2(fd(), STDERR_FILENO));
|
||||
}
|
||||
|
||||
void reset() {
|
||||
ASSERT_NE(-1, dup2(old_stderr_, STDERR_FILENO));
|
||||
ASSERT_EQ(0, close(old_stderr_));
|
||||
// Note: cannot restore prior setvbuf() setting.
|
||||
}
|
||||
|
||||
TemporaryFile temp_file_;
|
||||
int old_stderr_;
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
static void ExitSignalAbortHandler(int) {
|
||||
_exit(3);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void SuppressAbortUI() {
|
||||
#if defined(_WIN32)
|
||||
// We really just want to call _set_abort_behavior(0, _CALL_REPORTFAULT) to
|
||||
// suppress the Windows Error Reporting dialog box, but that API is not
|
||||
// available in the OS-supplied C Runtime, msvcrt.dll, that we currently
|
||||
// use (it is available in the Visual Studio C runtime).
|
||||
//
|
||||
// Instead, we setup a SIGABRT handler, which is called in abort() right
|
||||
// before calling Windows Error Reporting. In the handler, we exit the
|
||||
// process just like abort() does.
|
||||
ASSERT_NE(SIG_ERR, signal(SIGABRT, ExitSignalAbortHandler));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(logging, CHECK) {
|
||||
ASSERT_DEATH({SuppressAbortUI(); CHECK(false);}, "Check failed: false ");
|
||||
CHECK(true);
|
||||
|
||||
ASSERT_DEATH({SuppressAbortUI(); CHECK_EQ(0, 1);}, "Check failed: 0 == 1 ");
|
||||
CHECK_EQ(0, 0);
|
||||
|
||||
ASSERT_DEATH({SuppressAbortUI(); CHECK_STREQ("foo", "bar");},
|
||||
R"(Check failed: "foo" == "bar")");
|
||||
CHECK_STREQ("foo", "foo");
|
||||
|
||||
// Test whether CHECK() and CHECK_STREQ() have a dangling if with no else.
|
||||
bool flag = false;
|
||||
if (true)
|
||||
CHECK(true);
|
||||
else
|
||||
flag = true;
|
||||
EXPECT_FALSE(flag) << "CHECK macro probably has a dangling if with no else";
|
||||
|
||||
flag = false;
|
||||
if (true)
|
||||
CHECK_STREQ("foo", "foo");
|
||||
else
|
||||
flag = true;
|
||||
EXPECT_FALSE(flag) << "CHECK_STREQ probably has a dangling if with no else";
|
||||
}
|
||||
|
||||
std::string make_log_pattern(android::base::LogSeverity severity,
|
||||
const char* message) {
|
||||
static const char* log_characters = "VDIWEF";
|
||||
char log_char = log_characters[severity];
|
||||
std::string holder(__FILE__);
|
||||
return android::base::StringPrintf(
|
||||
"%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ %s:[[:digit:]]+] %s",
|
||||
log_char, basename(&holder[0]), message);
|
||||
}
|
||||
|
||||
TEST(logging, LOG) {
|
||||
ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
|
||||
|
||||
// We can't usefully check the output of any of these on Windows because we
|
||||
// don't have std::regex, but we can at least make sure we printed at least as
|
||||
// many characters are in the log message.
|
||||
{
|
||||
CapturedStderr cap;
|
||||
LOG(WARNING) << "foobar";
|
||||
ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
|
||||
|
||||
std::string output;
|
||||
android::base::ReadFdToString(cap.fd(), &output);
|
||||
ASSERT_GT(output.length(), strlen("foobar"));
|
||||
|
||||
#if !defined(_WIN32)
|
||||
std::regex message_regex(
|
||||
make_log_pattern(android::base::WARNING, "foobar"));
|
||||
ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
CapturedStderr cap;
|
||||
LOG(INFO) << "foobar";
|
||||
ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
|
||||
|
||||
std::string output;
|
||||
android::base::ReadFdToString(cap.fd(), &output);
|
||||
ASSERT_GT(output.length(), strlen("foobar"));
|
||||
|
||||
#if !defined(_WIN32)
|
||||
std::regex message_regex(
|
||||
make_log_pattern(android::base::INFO, "foobar"));
|
||||
ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
CapturedStderr cap;
|
||||
LOG(DEBUG) << "foobar";
|
||||
ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
|
||||
|
||||
std::string output;
|
||||
android::base::ReadFdToString(cap.fd(), &output);
|
||||
ASSERT_TRUE(output.empty());
|
||||
}
|
||||
|
||||
{
|
||||
android::base::ScopedLogSeverity severity(android::base::DEBUG);
|
||||
CapturedStderr cap;
|
||||
LOG(DEBUG) << "foobar";
|
||||
ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
|
||||
|
||||
std::string output;
|
||||
android::base::ReadFdToString(cap.fd(), &output);
|
||||
ASSERT_GT(output.length(), strlen("foobar"));
|
||||
|
||||
#if !defined(_WIN32)
|
||||
std::regex message_regex(
|
||||
make_log_pattern(android::base::DEBUG, "foobar"));
|
||||
ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Test whether LOG() saves and restores errno.
|
||||
{
|
||||
CapturedStderr cap;
|
||||
errno = 12345;
|
||||
LOG(INFO) << (errno = 67890);
|
||||
EXPECT_EQ(12345, errno) << "errno was not restored";
|
||||
|
||||
ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
|
||||
|
||||
std::string output;
|
||||
android::base::ReadFdToString(cap.fd(), &output);
|
||||
EXPECT_NE(nullptr, strstr(output.c_str(), "67890")) << output;
|
||||
|
||||
#if !defined(_WIN32)
|
||||
std::regex message_regex(
|
||||
make_log_pattern(android::base::INFO, "67890"));
|
||||
ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Test whether LOG() has a dangling if with no else.
|
||||
{
|
||||
CapturedStderr cap;
|
||||
|
||||
// Do the test two ways: once where we hypothesize that LOG()'s if
|
||||
// will evaluate to true (when severity is high enough) and once when we
|
||||
// expect it to evaluate to false (when severity is not high enough).
|
||||
bool flag = false;
|
||||
if (true)
|
||||
LOG(INFO) << "foobar";
|
||||
else
|
||||
flag = true;
|
||||
|
||||
EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
|
||||
|
||||
flag = false;
|
||||
if (true)
|
||||
LOG(VERBOSE) << "foobar";
|
||||
else
|
||||
flag = true;
|
||||
|
||||
EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(logging, PLOG) {
|
||||
{
|
||||
CapturedStderr cap;
|
||||
errno = ENOENT;
|
||||
PLOG(INFO) << "foobar";
|
||||
ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
|
||||
|
||||
std::string output;
|
||||
android::base::ReadFdToString(cap.fd(), &output);
|
||||
ASSERT_GT(output.length(), strlen("foobar"));
|
||||
|
||||
#if !defined(_WIN32)
|
||||
std::regex message_regex(make_log_pattern(
|
||||
android::base::INFO, "foobar: No such file or directory"));
|
||||
ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
TEST(logging, UNIMPLEMENTED) {
|
||||
{
|
||||
CapturedStderr cap;
|
||||
errno = ENOENT;
|
||||
UNIMPLEMENTED(ERROR);
|
||||
ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
|
||||
|
||||
std::string output;
|
||||
android::base::ReadFdToString(cap.fd(), &output);
|
||||
ASSERT_GT(output.length(), strlen("unimplemented"));
|
||||
|
||||
#if !defined(_WIN32)
|
||||
std::string expected_message =
|
||||
android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
|
||||
std::regex message_regex(
|
||||
make_log_pattern(android::base::ERROR, expected_message.c_str()));
|
||||
ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,78 +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 "android-base/parseint.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(parseint, signed_smoke) {
|
||||
int i;
|
||||
ASSERT_FALSE(android::base::ParseInt("x", &i));
|
||||
ASSERT_FALSE(android::base::ParseInt("123x", &i));
|
||||
|
||||
ASSERT_TRUE(android::base::ParseInt("123", &i));
|
||||
ASSERT_EQ(123, i);
|
||||
ASSERT_TRUE(android::base::ParseInt("-123", &i));
|
||||
ASSERT_EQ(-123, i);
|
||||
|
||||
short s;
|
||||
ASSERT_TRUE(android::base::ParseInt("1234", &s));
|
||||
ASSERT_EQ(1234, s);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
|
||||
ASSERT_EQ(12, i);
|
||||
ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
|
||||
ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
|
||||
}
|
||||
|
||||
TEST(parseint, unsigned_smoke) {
|
||||
unsigned int i;
|
||||
ASSERT_FALSE(android::base::ParseUint("x", &i));
|
||||
ASSERT_FALSE(android::base::ParseUint("123x", &i));
|
||||
|
||||
ASSERT_TRUE(android::base::ParseUint("123", &i));
|
||||
ASSERT_EQ(123u, i);
|
||||
ASSERT_FALSE(android::base::ParseUint("-123", &i));
|
||||
|
||||
unsigned short s;
|
||||
ASSERT_TRUE(android::base::ParseUint("1234", &s));
|
||||
ASSERT_EQ(1234u, s);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
|
||||
ASSERT_EQ(12u, i);
|
||||
ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
|
||||
ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
|
||||
}
|
||||
|
||||
TEST(parseint, no_implicit_octal) {
|
||||
int i;
|
||||
ASSERT_TRUE(android::base::ParseInt("0123", &i));
|
||||
ASSERT_EQ(123, i);
|
||||
|
||||
unsigned int u;
|
||||
ASSERT_TRUE(android::base::ParseUint("0123", &u));
|
||||
ASSERT_EQ(123u, u);
|
||||
}
|
||||
|
||||
TEST(parseint, explicit_hex) {
|
||||
int i;
|
||||
ASSERT_TRUE(android::base::ParseInt("0x123", &i));
|
||||
ASSERT_EQ(0x123, i);
|
||||
|
||||
unsigned int u;
|
||||
ASSERT_TRUE(android::base::ParseUint("0x123", &u));
|
||||
ASSERT_EQ(0x123u, u);
|
||||
}
|
||||
@@ -1,82 +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 "android-base/parsenetaddress.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "android-base/strings.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
bool ParseNetAddress(const std::string& address, std::string* host, int* port,
|
||||
std::string* canonical_address, std::string* error) {
|
||||
host->clear();
|
||||
|
||||
bool ipv6 = true;
|
||||
bool saw_port = false;
|
||||
size_t colons = std::count(address.begin(), address.end(), ':');
|
||||
size_t dots = std::count(address.begin(), address.end(), '.');
|
||||
std::string port_str;
|
||||
if (address[0] == '[') {
|
||||
// [::1]:123
|
||||
if (address.rfind("]:") == std::string::npos) {
|
||||
*error = StringPrintf("bad IPv6 address '%s'", address.c_str());
|
||||
return false;
|
||||
}
|
||||
*host = address.substr(1, (address.find("]:") - 1));
|
||||
port_str = address.substr(address.rfind("]:") + 2);
|
||||
saw_port = true;
|
||||
} else if (dots == 0 && colons >= 2 && colons <= 7) {
|
||||
// ::1
|
||||
*host = address;
|
||||
} else if (colons <= 1) {
|
||||
// 1.2.3.4 or some.accidental.domain.com
|
||||
ipv6 = false;
|
||||
std::vector<std::string> pieces = Split(address, ":");
|
||||
*host = pieces[0];
|
||||
if (pieces.size() > 1) {
|
||||
port_str = pieces[1];
|
||||
saw_port = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (host->empty()) {
|
||||
*error = StringPrintf("no host in '%s'", address.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (saw_port) {
|
||||
if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 ||
|
||||
*port > 65535) {
|
||||
*error = StringPrintf("bad port number '%s' in '%s'", port_str.c_str(),
|
||||
address.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canonical_address != nullptr) {
|
||||
*canonical_address =
|
||||
StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,127 +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 "android-base/parsenetaddress.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using android::base::ParseNetAddress;
|
||||
|
||||
TEST(ParseNetAddressTest, TestUrl) {
|
||||
std::string canonical, host, error;
|
||||
int port = 123;
|
||||
|
||||
EXPECT_TRUE(
|
||||
ParseNetAddress("www.google.com", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("www.google.com:123", canonical);
|
||||
EXPECT_EQ("www.google.com", host);
|
||||
EXPECT_EQ(123, port);
|
||||
|
||||
EXPECT_TRUE(
|
||||
ParseNetAddress("www.google.com:666", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("www.google.com:666", canonical);
|
||||
EXPECT_EQ("www.google.com", host);
|
||||
EXPECT_EQ(666, port);
|
||||
}
|
||||
|
||||
TEST(ParseNetAddressTest, TestIpv4) {
|
||||
std::string canonical, host, error;
|
||||
int port = 123;
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("1.2.3.4:123", canonical);
|
||||
EXPECT_EQ("1.2.3.4", host);
|
||||
EXPECT_EQ(123, port);
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("1.2.3.4:666", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("1.2.3.4:666", canonical);
|
||||
EXPECT_EQ("1.2.3.4", host);
|
||||
EXPECT_EQ(666, port);
|
||||
}
|
||||
|
||||
TEST(ParseNetAddressTest, TestIpv6) {
|
||||
std::string canonical, host, error;
|
||||
int port = 123;
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("::1", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("[::1]:123", canonical);
|
||||
EXPECT_EQ("::1", host);
|
||||
EXPECT_EQ(123, port);
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("fe80::200:5aee:feaa:20a2", &host, &port,
|
||||
&canonical, &error));
|
||||
EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical);
|
||||
EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
|
||||
EXPECT_EQ(123, port);
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("[::1]:666", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("[::1]:666", canonical);
|
||||
EXPECT_EQ("::1", host);
|
||||
EXPECT_EQ(666, port);
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("[fe80::200:5aee:feaa:20a2]:666", &host, &port,
|
||||
&canonical, &error));
|
||||
EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical);
|
||||
EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
|
||||
EXPECT_EQ(666, port);
|
||||
}
|
||||
|
||||
TEST(ParseNetAddressTest, TestInvalidAddress) {
|
||||
std::string canonical, host;
|
||||
int port;
|
||||
|
||||
std::string failure_cases[] = {
|
||||
// Invalid IPv4.
|
||||
"1.2.3.4:",
|
||||
"1.2.3.4::",
|
||||
":123",
|
||||
|
||||
// Invalid IPv6.
|
||||
":1",
|
||||
"::::::::1",
|
||||
"[::1",
|
||||
"[::1]",
|
||||
"[::1]:",
|
||||
"[::1]::",
|
||||
|
||||
// Invalid port.
|
||||
"1.2.3.4:-1",
|
||||
"1.2.3.4:0",
|
||||
"1.2.3.4:65536"
|
||||
"1.2.3.4:hello",
|
||||
"[::1]:-1",
|
||||
"[::1]:0",
|
||||
"[::1]:65536",
|
||||
"[::1]:hello",
|
||||
};
|
||||
|
||||
for (const auto& address : failure_cases) {
|
||||
// Failure should give some non-empty error string.
|
||||
std::string error;
|
||||
EXPECT_FALSE(ParseNetAddress(address, &host, &port, &canonical, &error));
|
||||
EXPECT_NE("", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Null canonical address argument.
|
||||
TEST(ParseNetAddressTest, TestNullCanonicalAddress) {
|
||||
std::string host, error;
|
||||
int port = 42;
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("www.google.com", &host, &port, nullptr, &error));
|
||||
EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, nullptr, &error));
|
||||
EXPECT_TRUE(ParseNetAddress("::1", &host, &port, nullptr, &error));
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 "android-base/stringprintf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
void StringAppendV(std::string* dst, const char* format, va_list ap) {
|
||||
// First try with a small fixed size buffer
|
||||
char space[1024];
|
||||
|
||||
// It's possible for methods that use a va_list to invalidate
|
||||
// the data in it upon use. The fix is to make a copy
|
||||
// of the structure before using it and use that copy instead.
|
||||
va_list backup_ap;
|
||||
va_copy(backup_ap, ap);
|
||||
int result = vsnprintf(space, sizeof(space), format, backup_ap);
|
||||
va_end(backup_ap);
|
||||
|
||||
if (result < static_cast<int>(sizeof(space))) {
|
||||
if (result >= 0) {
|
||||
// Normal case -- everything fit.
|
||||
dst->append(space, result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
// Just an error.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Increase the buffer size to the size requested by vsnprintf,
|
||||
// plus one for the closing \0.
|
||||
int length = result + 1;
|
||||
char* buf = new char[length];
|
||||
|
||||
// Restore the va_list before we use it again
|
||||
va_copy(backup_ap, ap);
|
||||
result = vsnprintf(buf, length, format, backup_ap);
|
||||
va_end(backup_ap);
|
||||
|
||||
if (result >= 0 && result < length) {
|
||||
// It fit
|
||||
dst->append(buf, result);
|
||||
}
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
std::string StringPrintf(const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
std::string result;
|
||||
StringAppendV(&result, fmt, ap);
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
|
||||
void StringAppendF(std::string* dst, const char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
StringAppendV(dst, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 "android-base/stringprintf.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
TEST(StringPrintfTest, HexSizeT) {
|
||||
size_t size = 0x00107e59;
|
||||
EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
|
||||
EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, StringAppendF) {
|
||||
std::string s("a");
|
||||
android::base::StringAppendF(&s, "b");
|
||||
EXPECT_EQ("ab", s);
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, Errno) {
|
||||
errno = 123;
|
||||
android::base::StringPrintf("hello %s", "world");
|
||||
EXPECT_EQ(123, errno);
|
||||
}
|
||||
|
||||
void TestN(size_t n) {
|
||||
char* buf = new char[n + 1];
|
||||
memset(buf, 'x', n);
|
||||
buf[n] = '\0';
|
||||
std::string s(android::base::StringPrintf("%s", buf));
|
||||
EXPECT_EQ(buf, s);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, At1023) {
|
||||
TestN(1023);
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, At1024) {
|
||||
TestN(1024);
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, At1025) {
|
||||
TestN(1025);
|
||||
}
|
||||
@@ -1,104 +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 "android-base/strings.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
#define CHECK_NE(a, b) \
|
||||
if ((a) == (b)) abort();
|
||||
|
||||
std::vector<std::string> Split(const std::string& s,
|
||||
const std::string& delimiters) {
|
||||
CHECK_NE(delimiters.size(), 0U);
|
||||
|
||||
std::vector<std::string> result;
|
||||
|
||||
size_t base = 0;
|
||||
size_t found;
|
||||
do {
|
||||
found = s.find_first_of(delimiters, base);
|
||||
result.push_back(s.substr(base, found - base));
|
||||
base = found + 1;
|
||||
} while (found != s.npos);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Trim(const std::string& s) {
|
||||
std::string result;
|
||||
|
||||
if (s.size() == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t start_index = 0;
|
||||
size_t end_index = s.size() - 1;
|
||||
|
||||
// Skip initial whitespace.
|
||||
while (start_index < s.size()) {
|
||||
if (!isspace(s[start_index])) {
|
||||
break;
|
||||
}
|
||||
start_index++;
|
||||
}
|
||||
|
||||
// Skip terminating whitespace.
|
||||
while (end_index >= start_index) {
|
||||
if (!isspace(s[end_index])) {
|
||||
break;
|
||||
}
|
||||
end_index--;
|
||||
}
|
||||
|
||||
// All spaces, no beef.
|
||||
if (end_index < start_index) {
|
||||
return "";
|
||||
}
|
||||
// Start_index is the first non-space, end_index is the last one.
|
||||
return s.substr(start_index, end_index - start_index + 1);
|
||||
}
|
||||
|
||||
// These cases are probably the norm, so we mark them extern in the header to
|
||||
// aid compile time and binary size.
|
||||
template std::string Join(const std::vector<std::string>&, char);
|
||||
template std::string Join(const std::vector<const char*>&, char);
|
||||
template std::string Join(const std::vector<std::string>&, const std::string&);
|
||||
template std::string Join(const std::vector<const char*>&, const std::string&);
|
||||
|
||||
bool StartsWith(const std::string& s, const char* prefix) {
|
||||
return s.compare(0, strlen(prefix), prefix) == 0;
|
||||
}
|
||||
|
||||
bool EndsWith(const std::string& s, const char* suffix) {
|
||||
size_t suffix_length = strlen(suffix);
|
||||
size_t string_length = s.size();
|
||||
if (suffix_length > string_length) {
|
||||
return false;
|
||||
}
|
||||
size_t offset = string_length - suffix_length;
|
||||
return s.compare(offset, suffix_length, suffix) == 0;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,177 +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 "android-base/strings.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
TEST(strings, split_empty) {
|
||||
std::vector<std::string> parts = android::base::Split("", ",");
|
||||
ASSERT_EQ(1U, parts.size());
|
||||
ASSERT_EQ("", parts[0]);
|
||||
}
|
||||
|
||||
TEST(strings, split_single) {
|
||||
std::vector<std::string> parts = android::base::Split("foo", ",");
|
||||
ASSERT_EQ(1U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
}
|
||||
|
||||
TEST(strings, split_simple) {
|
||||
std::vector<std::string> parts = android::base::Split("foo,bar,baz", ",");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("bar", parts[1]);
|
||||
ASSERT_EQ("baz", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, split_with_empty_part) {
|
||||
std::vector<std::string> parts = android::base::Split("foo,,bar", ",");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("", parts[1]);
|
||||
ASSERT_EQ("bar", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, split_null_char) {
|
||||
std::vector<std::string> parts =
|
||||
android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
|
||||
ASSERT_EQ(2U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("bar", parts[1]);
|
||||
}
|
||||
|
||||
TEST(strings, split_any) {
|
||||
std::vector<std::string> parts = android::base::Split("foo:bar,baz", ",:");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("bar", parts[1]);
|
||||
ASSERT_EQ("baz", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, split_any_with_empty_part) {
|
||||
std::vector<std::string> parts = android::base::Split("foo:,bar", ",:");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("", parts[1]);
|
||||
ASSERT_EQ("bar", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, trim_empty) {
|
||||
ASSERT_EQ("", android::base::Trim(""));
|
||||
}
|
||||
|
||||
TEST(strings, trim_already_trimmed) {
|
||||
ASSERT_EQ("foo", android::base::Trim("foo"));
|
||||
}
|
||||
|
||||
TEST(strings, trim_left) {
|
||||
ASSERT_EQ("foo", android::base::Trim(" foo"));
|
||||
}
|
||||
|
||||
TEST(strings, trim_right) {
|
||||
ASSERT_EQ("foo", android::base::Trim("foo "));
|
||||
}
|
||||
|
||||
TEST(strings, trim_both) {
|
||||
ASSERT_EQ("foo", android::base::Trim(" foo "));
|
||||
}
|
||||
|
||||
TEST(strings, trim_no_trim_middle) {
|
||||
ASSERT_EQ("foo bar", android::base::Trim("foo bar"));
|
||||
}
|
||||
|
||||
TEST(strings, trim_other_whitespace) {
|
||||
ASSERT_EQ("foo", android::base::Trim("\v\tfoo\n\f"));
|
||||
}
|
||||
|
||||
TEST(strings, join_nothing) {
|
||||
std::vector<std::string> list = {};
|
||||
ASSERT_EQ("", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_single) {
|
||||
std::vector<std::string> list = {"foo"};
|
||||
ASSERT_EQ("foo", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_simple) {
|
||||
std::vector<std::string> list = {"foo", "bar", "baz"};
|
||||
ASSERT_EQ("foo,bar,baz", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_separator_in_vector) {
|
||||
std::vector<std::string> list = {",", ","};
|
||||
ASSERT_EQ(",,,", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_simple_ints) {
|
||||
std::set<int> list = {1, 2, 3};
|
||||
ASSERT_EQ("1,2,3", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_unordered_set) {
|
||||
std::unordered_set<int> list = {1, 2};
|
||||
ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
|
||||
"2,1" == android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, startswith_empty) {
|
||||
ASSERT_FALSE(android::base::StartsWith("", "foo"));
|
||||
ASSERT_TRUE(android::base::StartsWith("", ""));
|
||||
}
|
||||
|
||||
TEST(strings, startswith_simple) {
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", ""));
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", "f"));
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", "fo"));
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", "foo"));
|
||||
}
|
||||
|
||||
TEST(strings, startswith_prefix_too_long) {
|
||||
ASSERT_FALSE(android::base::StartsWith("foo", "foobar"));
|
||||
}
|
||||
|
||||
TEST(strings, startswith_contains_prefix) {
|
||||
ASSERT_FALSE(android::base::StartsWith("foobar", "oba"));
|
||||
ASSERT_FALSE(android::base::StartsWith("foobar", "bar"));
|
||||
}
|
||||
|
||||
TEST(strings, endswith_empty) {
|
||||
ASSERT_FALSE(android::base::EndsWith("", "foo"));
|
||||
ASSERT_TRUE(android::base::EndsWith("", ""));
|
||||
}
|
||||
|
||||
TEST(strings, endswith_simple) {
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", ""));
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", "o"));
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", "oo"));
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", "foo"));
|
||||
}
|
||||
|
||||
TEST(strings, endswith_prefix_too_long) {
|
||||
ASSERT_FALSE(android::base::EndsWith("foo", "foobar"));
|
||||
}
|
||||
|
||||
TEST(strings, endswith_contains_prefix) {
|
||||
ASSERT_FALSE(android::base::EndsWith("foobar", "oba"));
|
||||
ASSERT_FALSE(android::base::EndsWith("foobar", "foo"));
|
||||
}
|
||||
@@ -1,25 +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 <gtest/gtest.h>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
android::base::InitLogging(argv, android::base::StderrLogger);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -1,102 +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 "android-base/logging.h"
|
||||
#include "android-base/test_utils.h"
|
||||
#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
int mkstemp(char* template_name) {
|
||||
if (_mktemp(template_name) == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
// Use open() to match the close() that TemporaryFile's destructor does.
|
||||
// Use O_BINARY to match base file APIs.
|
||||
return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
|
||||
S_IRUSR | S_IWUSR);
|
||||
}
|
||||
|
||||
char* mkdtemp(char* template_name) {
|
||||
if (_mktemp(template_name) == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (_mkdir(template_name) == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
return template_name;
|
||||
}
|
||||
#endif
|
||||
|
||||
static std::string GetSystemTempDir() {
|
||||
#if defined(__ANDROID__)
|
||||
return "/data/local/tmp";
|
||||
#elif defined(_WIN32)
|
||||
char tmp_dir[MAX_PATH];
|
||||
DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
|
||||
CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
|
||||
CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
|
||||
|
||||
// GetTempPath() returns a path with a trailing slash, but init()
|
||||
// does not expect that, so remove it.
|
||||
CHECK_EQ(tmp_dir[result - 1], '\\');
|
||||
tmp_dir[result - 1] = '\0';
|
||||
return tmp_dir;
|
||||
#else
|
||||
return "/tmp";
|
||||
#endif
|
||||
}
|
||||
|
||||
TemporaryFile::TemporaryFile() {
|
||||
init(GetSystemTempDir());
|
||||
}
|
||||
|
||||
TemporaryFile::~TemporaryFile() {
|
||||
close(fd);
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
void TemporaryFile::init(const std::string& tmp_dir) {
|
||||
snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
|
||||
OS_PATH_SEPARATOR);
|
||||
fd = mkstemp(path);
|
||||
}
|
||||
|
||||
TemporaryDir::TemporaryDir() {
|
||||
init(GetSystemTempDir());
|
||||
}
|
||||
|
||||
TemporaryDir::~TemporaryDir() {
|
||||
rmdir(path);
|
||||
}
|
||||
|
||||
bool TemporaryDir::init(const std::string& tmp_dir) {
|
||||
snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(),
|
||||
OS_PATH_SEPARATOR);
|
||||
return (mkdtemp(path) != nullptr);
|
||||
}
|
||||
@@ -1,187 +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 <windows.h>
|
||||
|
||||
#include "android-base/utf8.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
|
||||
static void SetErrnoFromLastError() {
|
||||
switch (GetLastError()) {
|
||||
case ERROR_NO_UNICODE_TRANSLATION:
|
||||
errno = EILSEQ;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
|
||||
utf8->clear();
|
||||
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Consider using std::wstring_convert once libcxx is supported on
|
||||
// Windows.
|
||||
|
||||
// Only Vista or later has this flag that causes WideCharToMultiByte() to
|
||||
// return an error on invalid characters.
|
||||
const DWORD flags =
|
||||
#if (WINVER >= 0x0600)
|
||||
WC_ERR_INVALID_CHARS;
|
||||
#else
|
||||
0;
|
||||
#endif
|
||||
|
||||
const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
|
||||
NULL, 0, NULL, NULL);
|
||||
if (chars_required <= 0) {
|
||||
SetErrnoFromLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// This could potentially throw a std::bad_alloc exception.
|
||||
utf8->resize(chars_required);
|
||||
|
||||
const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
|
||||
&(*utf8)[0], chars_required, NULL,
|
||||
NULL);
|
||||
if (result != chars_required) {
|
||||
SetErrnoFromLastError();
|
||||
CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
|
||||
<< " chars to buffer of " << chars_required << " chars";
|
||||
utf8->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
|
||||
// Compute string length of NULL-terminated string with wcslen().
|
||||
return WideToUTF8(utf16, wcslen(utf16), utf8);
|
||||
}
|
||||
|
||||
bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
|
||||
// Use the stored length of the string which allows embedded NULL characters
|
||||
// to be converted.
|
||||
return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
|
||||
}
|
||||
|
||||
// Internal helper function that takes MultiByteToWideChar() flags.
|
||||
static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
|
||||
const DWORD flags) {
|
||||
utf16->clear();
|
||||
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Consider using std::wstring_convert once libcxx is supported on
|
||||
// Windows.
|
||||
const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
|
||||
NULL, 0);
|
||||
if (chars_required <= 0) {
|
||||
SetErrnoFromLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// This could potentially throw a std::bad_alloc exception.
|
||||
utf16->resize(chars_required);
|
||||
|
||||
const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
|
||||
&(*utf16)[0], chars_required);
|
||||
if (result != chars_required) {
|
||||
SetErrnoFromLastError();
|
||||
CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
|
||||
<< " chars to buffer of " << chars_required << " chars";
|
||||
utf16->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
|
||||
// If strictly interpreting as UTF-8 succeeds, return success.
|
||||
if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int saved_errno = errno;
|
||||
|
||||
// Fallback to non-strict interpretation, allowing invalid characters and
|
||||
// converting as best as possible, and return false to signify a problem.
|
||||
(void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
|
||||
errno = saved_errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
|
||||
// Compute string length of NULL-terminated string with strlen().
|
||||
return UTF8ToWide(utf8, strlen(utf8), utf16);
|
||||
}
|
||||
|
||||
bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
|
||||
// Use the stored length of the string which allows embedded NULL characters
|
||||
// to be converted.
|
||||
return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
|
||||
}
|
||||
|
||||
// Versions of standard library APIs that support UTF-8 strings.
|
||||
namespace utf8 {
|
||||
|
||||
int open(const char* name, int flags, ...) {
|
||||
std::wstring name_utf16;
|
||||
if (!UTF8ToWide(name, &name_utf16)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mode = 0;
|
||||
if ((flags & O_CREAT) != 0) {
|
||||
va_list args;
|
||||
va_start(args, flags);
|
||||
mode = va_arg(args, int);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
return _wopen(name_utf16.c_str(), flags, mode);
|
||||
}
|
||||
|
||||
int unlink(const char* name) {
|
||||
std::wstring name_utf16;
|
||||
if (!UTF8ToWide(name, &name_utf16)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _wunlink(name_utf16.c_str());
|
||||
}
|
||||
|
||||
} // namespace utf8
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,412 +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 "android-base/utf8.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
|
||||
std::wstring wide;
|
||||
|
||||
errno = 0;
|
||||
|
||||
// Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
|
||||
// error. Concatenate two C/C++ literal string constants to prevent the
|
||||
// compiler from giving an error about "\xa2af" containing a "hex escape
|
||||
// sequence out of range".
|
||||
EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
|
||||
|
||||
EXPECT_EQ(EILSEQ, errno);
|
||||
|
||||
// Even if an invalid character is encountered, UTF8ToWide() should still do
|
||||
// its best to convert the rest of the string. sysdeps_win32.cpp:
|
||||
// _console_write_utf8() depends on this behavior.
|
||||
//
|
||||
// Thus, we verify that the valid characters are converted, but we ignore the
|
||||
// specific replacement character that UTF8ToWide() may replace the invalid
|
||||
// UTF-8 characters with because we want to allow that to change if the
|
||||
// implementation changes.
|
||||
EXPECT_EQ(0U, wide.find(L"before"));
|
||||
const wchar_t after_wide[] = L"after";
|
||||
EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
|
||||
}
|
||||
|
||||
// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
|
||||
|
||||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// The tests below from utf_string_conversions_unittest.cc check for this
|
||||
// preprocessor symbol, so define it, as it is appropriate for Windows.
|
||||
#define WCHAR_T_IS_UTF16
|
||||
static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
|
||||
|
||||
// The tests below from utf_string_conversions_unittest.cc call versions of
|
||||
// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
|
||||
// stub implementations with that signature. These are just for testing and
|
||||
// should not be moved to base because they assert/expect no errors which is
|
||||
// probably not a good idea (or at least it is something that should be left
|
||||
// up to the caller, not a base library).
|
||||
|
||||
static std::wstring UTF8ToWide(const std::string& utf8) {
|
||||
std::wstring utf16;
|
||||
EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
|
||||
return utf16;
|
||||
}
|
||||
|
||||
static std::string WideToUTF8(const std::wstring& utf16) {
|
||||
std::string utf8;
|
||||
EXPECT_TRUE(WideToUTF8(utf16, &utf8));
|
||||
return utf8;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const wchar_t* const kConvertRoundtripCases[] = {
|
||||
L"Google Video",
|
||||
// "网页 图片 资讯更多 »"
|
||||
L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
|
||||
// "Παγκόσμιος Ιστός"
|
||||
L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
|
||||
L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
|
||||
// "Поиск страниц на русском"
|
||||
L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
|
||||
L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
|
||||
L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
|
||||
// "전체서비스"
|
||||
L"\xc804\xccb4\xc11c\xbe44\xc2a4",
|
||||
|
||||
// Test characters that take more than 16 bits. This will depend on whether
|
||||
// wchar_t is 16 or 32 bits.
|
||||
#if defined(WCHAR_T_IS_UTF16)
|
||||
L"\xd800\xdf00",
|
||||
// ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
|
||||
L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
|
||||
#elif defined(WCHAR_T_IS_UTF32)
|
||||
L"\x10300",
|
||||
// ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
|
||||
L"\x11d40\x11d41\x11d42\x11d43\x11d44",
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
|
||||
// we round-trip all the wide strings through UTF-8 to make sure everything
|
||||
// agrees on the conversion. This uses the stream operators to test them
|
||||
// simultaneously.
|
||||
for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
|
||||
std::ostringstream utf8;
|
||||
utf8 << WideToUTF8(kConvertRoundtripCases[i]);
|
||||
std::wostringstream wide;
|
||||
wide << UTF8ToWide(utf8.str());
|
||||
|
||||
EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
|
||||
// An empty std::wstring should be converted to an empty std::string,
|
||||
// and vice versa.
|
||||
std::wstring wempty;
|
||||
std::string empty;
|
||||
EXPECT_EQ(empty, WideToUTF8(wempty));
|
||||
EXPECT_EQ(wempty, UTF8ToWide(empty));
|
||||
}
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
|
||||
struct UTF8ToWideCase {
|
||||
const char* utf8;
|
||||
const wchar_t* wide;
|
||||
bool success;
|
||||
} convert_cases[] = {
|
||||
// Regular UTF-8 input.
|
||||
{"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
|
||||
// Non-character is passed through.
|
||||
{"\xef\xbf\xbfHello", L"\xffffHello", true},
|
||||
// Truncated UTF-8 sequence.
|
||||
{"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
|
||||
// Truncated off the end.
|
||||
{"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
|
||||
// Non-shortest-form UTF-8.
|
||||
{"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
|
||||
// This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
|
||||
// Note that for whatever reason, this test fails on Windows XP.
|
||||
{"\xed\xb0\x80", L"\xfffd", false},
|
||||
// Non-BMP characters. The second is a non-character regarded as valid.
|
||||
// The result will either be in UTF-16 or UTF-32.
|
||||
#if defined(WCHAR_T_IS_UTF16)
|
||||
{"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
|
||||
{"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
|
||||
#elif defined(WCHAR_T_IS_UTF32)
|
||||
{"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
|
||||
{"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
|
||||
#endif
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(convert_cases); i++) {
|
||||
std::wstring converted;
|
||||
errno = 0;
|
||||
const bool success = UTF8ToWide(convert_cases[i].utf8,
|
||||
strlen(convert_cases[i].utf8),
|
||||
&converted);
|
||||
EXPECT_EQ(convert_cases[i].success, success);
|
||||
// The original test always compared expected and converted, but don't do
|
||||
// that because our implementation of UTF8ToWide() does not guarantee to
|
||||
// produce the same output in error situations.
|
||||
if (success) {
|
||||
std::wstring expected(convert_cases[i].wide);
|
||||
EXPECT_EQ(expected, converted);
|
||||
} else {
|
||||
EXPECT_EQ(EILSEQ, errno);
|
||||
}
|
||||
}
|
||||
|
||||
// Manually test an embedded NULL.
|
||||
std::wstring converted;
|
||||
EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
|
||||
ASSERT_EQ(3U, converted.length());
|
||||
EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
|
||||
EXPECT_EQ('Z', converted[1]);
|
||||
EXPECT_EQ('\t', converted[2]);
|
||||
|
||||
// Make sure that conversion replaces, not appends.
|
||||
EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
|
||||
ASSERT_EQ(1U, converted.length());
|
||||
EXPECT_EQ('B', converted[0]);
|
||||
}
|
||||
|
||||
#if defined(WCHAR_T_IS_UTF16)
|
||||
// This test is only valid when wchar_t == UTF-16.
|
||||
TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
|
||||
struct WideToUTF8Case {
|
||||
const wchar_t* utf16;
|
||||
const char* utf8;
|
||||
bool success;
|
||||
} convert_cases[] = {
|
||||
// Regular UTF-16 input.
|
||||
{L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
|
||||
// Test a non-BMP character.
|
||||
{L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
|
||||
// Non-characters are passed through.
|
||||
{L"\xffffHello", "\xEF\xBF\xBFHello", true},
|
||||
{L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
|
||||
// The first character is a truncated UTF-16 character.
|
||||
// Note that for whatever reason, this test fails on Windows XP.
|
||||
{L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
|
||||
#if (WINVER >= 0x0600)
|
||||
// Only Vista and later has a new API/flag that correctly returns false.
|
||||
false
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
},
|
||||
// Truncated at the end.
|
||||
// Note that for whatever reason, this test fails on Windows XP.
|
||||
{L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
|
||||
#if (WINVER >= 0x0600)
|
||||
// Only Vista and later has a new API/flag that correctly returns false.
|
||||
false
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(convert_cases); i++) {
|
||||
std::string converted;
|
||||
errno = 0;
|
||||
const bool success = WideToUTF8(convert_cases[i].utf16,
|
||||
wcslen(convert_cases[i].utf16),
|
||||
&converted);
|
||||
EXPECT_EQ(convert_cases[i].success, success);
|
||||
// The original test always compared expected and converted, but don't do
|
||||
// that because our implementation of WideToUTF8() does not guarantee to
|
||||
// produce the same output in error situations.
|
||||
if (success) {
|
||||
std::string expected(convert_cases[i].utf8);
|
||||
EXPECT_EQ(expected, converted);
|
||||
} else {
|
||||
EXPECT_EQ(EILSEQ, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(WCHAR_T_IS_UTF32)
|
||||
// This test is only valid when wchar_t == UTF-32.
|
||||
TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
|
||||
struct WideToUTF8Case {
|
||||
const wchar_t* utf32;
|
||||
const char* utf8;
|
||||
bool success;
|
||||
} convert_cases[] = {
|
||||
// Regular 16-bit input.
|
||||
{L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
|
||||
// Test a non-BMP character.
|
||||
{L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
|
||||
// Non-characters are passed through.
|
||||
{L"\xffffHello", "\xEF\xBF\xBFHello", true},
|
||||
{L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
|
||||
// Invalid Unicode code points.
|
||||
{L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
|
||||
// The first character is a truncated UTF-16 character.
|
||||
{L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
|
||||
{L"\xdc01Hello", "\xef\xbf\xbdHello", false},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(convert_cases); i++) {
|
||||
std::string converted;
|
||||
EXPECT_EQ(convert_cases[i].success,
|
||||
WideToUTF8(convert_cases[i].utf32,
|
||||
wcslen(convert_cases[i].utf32),
|
||||
&converted));
|
||||
std::string expected(convert_cases[i].utf8);
|
||||
EXPECT_EQ(expected, converted);
|
||||
}
|
||||
}
|
||||
#endif // defined(WCHAR_T_IS_UTF32)
|
||||
|
||||
// The test below uses these types and functions, so just do enough to get the
|
||||
// test running.
|
||||
typedef wchar_t char16;
|
||||
typedef std::wstring string16;
|
||||
|
||||
template<typename T>
|
||||
static void* WriteInto(T* t, size_t size) {
|
||||
// std::(w)string::resize() already includes space for a NULL terminator.
|
||||
t->resize(size - 1);
|
||||
return &(*t)[0];
|
||||
}
|
||||
|
||||
// A stub implementation that calls a helper from above, just to get the test
|
||||
// below working. This is just for testing and should not be moved to base
|
||||
// because this ignores errors which is probably not a good idea, plus it takes
|
||||
// a string16 type which we don't really have.
|
||||
static std::string UTF16ToUTF8(const string16& utf16) {
|
||||
return WideToUTF8(utf16);
|
||||
}
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertMultiString) {
|
||||
static char16 multi16[] = {
|
||||
'f', 'o', 'o', '\0',
|
||||
'b', 'a', 'r', '\0',
|
||||
'b', 'a', 'z', '\0',
|
||||
'\0'
|
||||
};
|
||||
static char multi[] = {
|
||||
'f', 'o', 'o', '\0',
|
||||
'b', 'a', 'r', '\0',
|
||||
'b', 'a', 'z', '\0',
|
||||
'\0'
|
||||
};
|
||||
string16 multistring16;
|
||||
memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
|
||||
sizeof(multi16));
|
||||
EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
|
||||
std::string expected;
|
||||
memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
|
||||
EXPECT_EQ(arraysize(multi) - 1, expected.length());
|
||||
const std::string& converted = UTF16ToUTF8(multistring16);
|
||||
EXPECT_EQ(arraysize(multi) - 1, converted.length());
|
||||
EXPECT_EQ(expected, converted);
|
||||
}
|
||||
|
||||
// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
|
||||
// and SysUTF8ToWide(), so these are stub implementations that call the helpers
|
||||
// above. These are just for testing and should not be moved to base because
|
||||
// they ignore errors which is probably not a good idea.
|
||||
|
||||
static std::string SysWideToUTF8(const std::wstring& utf16) {
|
||||
return WideToUTF8(utf16);
|
||||
}
|
||||
|
||||
static std::wstring SysUTF8ToWide(const std::string& utf8) {
|
||||
return UTF8ToWide(utf8);
|
||||
}
|
||||
|
||||
// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
|
||||
|
||||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifdef WCHAR_T_IS_UTF32
|
||||
static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
|
||||
#else
|
||||
static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
|
||||
#endif
|
||||
|
||||
TEST(SysStrings, SysWideToUTF8) {
|
||||
EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
|
||||
EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
|
||||
|
||||
// >16 bits
|
||||
EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
|
||||
|
||||
// Error case. When Windows finds a UTF-16 character going off the end of
|
||||
// a string, it just converts that literal value to UTF-8, even though this
|
||||
// is invalid.
|
||||
//
|
||||
// This is what XP does, but Vista has different behavior, so we don't bother
|
||||
// verifying it:
|
||||
// EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
|
||||
// SysWideToUTF8(L"\x4f60\xd800zyxw"));
|
||||
|
||||
// Test embedded NULLs.
|
||||
std::wstring wide_null(L"a");
|
||||
wide_null.push_back(0);
|
||||
wide_null.push_back('b');
|
||||
|
||||
std::string expected_null("a");
|
||||
expected_null.push_back(0);
|
||||
expected_null.push_back('b');
|
||||
|
||||
EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
|
||||
}
|
||||
|
||||
TEST(SysStrings, SysUTF8ToWide) {
|
||||
EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
|
||||
EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
|
||||
// >16 bits
|
||||
EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
|
||||
|
||||
// Error case. When Windows finds an invalid UTF-8 character, it just skips
|
||||
// it. This seems weird because it's inconsistent with the reverse conversion.
|
||||
//
|
||||
// This is what XP does, but Vista has different behavior, so we don't bother
|
||||
// verifying it:
|
||||
// EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
|
||||
|
||||
// Test embedded NULLs.
|
||||
std::string utf8_null("a");
|
||||
utf8_null.push_back(0);
|
||||
utf8_null.push_back('b');
|
||||
|
||||
std::wstring expected_null(L"a");
|
||||
expected_null.push_back(0);
|
||||
expected_null.push_back('b');
|
||||
|
||||
EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
@@ -1,74 +0,0 @@
|
||||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2001-2005 Hewlett-Packard Co
|
||||
Copyright (C) 2007 David Mosberger-Tang
|
||||
Contributed by David Mosberger-Tang <dmosberger@gmail.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
/* Compiler specific useful bits that are used in libunwind, and also in the
|
||||
* tests. */
|
||||
|
||||
#ifndef COMPILER_H
|
||||
#define COMPILER_H
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define ALIGNED(x) __attribute__((aligned(x)))
|
||||
# define CONST_ATTR __attribute__((__const__))
|
||||
# define UNUSED __attribute__((unused))
|
||||
# define NOINLINE __attribute__((noinline))
|
||||
# define NORETURN __attribute__((noreturn))
|
||||
# define ALIAS(name) __attribute__((alias (#name)))
|
||||
# if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 2)
|
||||
# define ALWAYS_INLINE inline __attribute__((always_inline))
|
||||
# define HIDDEN __attribute__((visibility ("hidden")))
|
||||
# define PROTECTED __attribute__((visibility ("protected")))
|
||||
# else
|
||||
# define ALWAYS_INLINE
|
||||
# define HIDDEN
|
||||
# define PROTECTED
|
||||
# endif
|
||||
# define WEAK __attribute__((weak))
|
||||
# if (__GNUC__ >= 3)
|
||||
# define likely(x) __builtin_expect ((x), 1)
|
||||
# define unlikely(x) __builtin_expect ((x), 0)
|
||||
# else
|
||||
# define likely(x) (x)
|
||||
# define unlikely(x) (x)
|
||||
# endif
|
||||
#else
|
||||
# define ALIGNED(x)
|
||||
# define ALWAYS_INLINE
|
||||
# define CONST_ATTR
|
||||
# define UNUSED
|
||||
# define NOINLINE
|
||||
# define NORETURN
|
||||
# define ALIAS(name)
|
||||
# define HIDDEN
|
||||
# define PROTECTED
|
||||
# define WEAK
|
||||
# define likely(x) (x)
|
||||
# define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
|
||||
|
||||
#endif /* COMPILER_H */
|
||||
@@ -1,255 +0,0 @@
|
||||
/* include/config.h. Generated from config.h.in by configure. */
|
||||
/* include/config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Block signals before mutex operations */
|
||||
/* #undef CONFIG_BLOCK_SIGNALS */
|
||||
|
||||
/* Enable Debug Frame */
|
||||
#define CONFIG_DEBUG_FRAME 1
|
||||
|
||||
/* Support for Microsoft ABI extensions */
|
||||
/* This is required to understand floating point registers on x86-64 */
|
||||
#define CONFIG_MSABI_SUPPORT 1
|
||||
|
||||
/* Define to 1 if you want every memory access validated */
|
||||
#define CONSERVATIVE_CHECKS 1
|
||||
|
||||
/* Allocate large structures rather than place them on the stack. */
|
||||
#define CONSERVE_STACK /**/
|
||||
|
||||
/* Define to 1 if you have the <asm/ptrace_offsets.h> header file. */
|
||||
/* #undef HAVE_ASM_PTRACE_OFFSETS_H */
|
||||
|
||||
/* Define to 1 if you have the <atomic_ops.h> header file. */
|
||||
/* #undef HAVE_ATOMIC_OPS_H */
|
||||
|
||||
/* Define to 1 if you have the <byteswap.h> header file. */
|
||||
#define HAVE_BYTESWAP_H 1
|
||||
|
||||
/* Define to 1 if you have the declaration of `PTRACE_CONT', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_PTRACE_CONT 1
|
||||
|
||||
/* Define to 1 if you have the declaration of `PTRACE_POKEDATA', and to 0 if
|
||||
you don't. */
|
||||
#define HAVE_DECL_PTRACE_POKEDATA 1
|
||||
|
||||
/* Define to 1 if you have the declaration of `PTRACE_POKEUSER', and to 0 if
|
||||
you don't. */
|
||||
#if defined(__aarch64__) || defined(__mips__)
|
||||
#define HAVE_DECL_PTRACE_POKEUSER 0
|
||||
#else
|
||||
#define HAVE_DECL_PTRACE_POKEUSER 1
|
||||
#endif
|
||||
|
||||
/* Define to 1 if you have the declaration of `PTRACE_SINGLESTEP', and to 0 if
|
||||
you don't. */
|
||||
#define HAVE_DECL_PTRACE_SINGLESTEP 1
|
||||
|
||||
/* Define to 1 if you have the declaration of `PTRACE_SYSCALL', and to 0 if
|
||||
you don't. */
|
||||
#define HAVE_DECL_PTRACE_SYSCALL 1
|
||||
|
||||
/* Define to 1 if you have the declaration of `PTRACE_TRACEME', and to 0 if
|
||||
you don't. */
|
||||
#define HAVE_DECL_PTRACE_TRACEME 1
|
||||
|
||||
/* Define to 1 if you have the declaration of `PT_CONTINUE', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_PT_CONTINUE 0
|
||||
|
||||
/* Define to 1 if you have the declaration of `PT_GETFPREGS', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_PT_GETFPREGS 0
|
||||
|
||||
/* Define to 1 if you have the declaration of `PT_GETREGS', and to 0 if you
|
||||
don't. */
|
||||
#if defined(__mips__)
|
||||
#define HAVE_DECL_PT_GETREGS 1
|
||||
#else
|
||||
#define HAVE_DECL_PT_GETREGS 0
|
||||
#endif
|
||||
|
||||
/* Define to 1 if you have the declaration of `PT_GETREGSET', and to 0 if you
|
||||
don't. */
|
||||
#if defined(__aarch64__)
|
||||
#define HAVE_DECL_PT_GETREGSET 1
|
||||
#else
|
||||
#define HAVE_DECL_PT_GETREGSET 0
|
||||
#endif
|
||||
|
||||
/* Define to 1 if you have the declaration of `PT_IO', and to 0 if you don't.
|
||||
*/
|
||||
#define HAVE_DECL_PT_IO 0
|
||||
|
||||
/* Define to 1 if you have the declaration of `PT_STEP', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_PT_STEP 0
|
||||
|
||||
/* Define to 1 if you have the declaration of `PT_SYSCALL', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_PT_SYSCALL 0
|
||||
|
||||
/* Define to 1 if you have the declaration of `PT_TRACE_ME', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_PT_TRACE_ME 0
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Define to 1 if you have the `dlmodinfo' function. */
|
||||
#define HAVE_DLMODINFO 1
|
||||
|
||||
/* Define to 1 if you have the `dl_iterate_phdr' function. */
|
||||
#define HAVE_DL_ITERATE_PHDR 1
|
||||
|
||||
/* Define to 1 if you have the `dl_phdr_removals_counter' function. */
|
||||
#define HAVE_DL_PHDR_REMOVALS_COUNTER 1
|
||||
|
||||
/* Define to 1 if you have the <elf.h> header file. */
|
||||
#define HAVE_ELF_H 1
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#define HAVE_ENDIAN_H 1
|
||||
|
||||
/* Define to 1 if you have the <execinfo.h> header file. */
|
||||
/* #undef HAVE_EXECINFO_H */
|
||||
|
||||
/* Define to 1 if you have the `getunwind' function. */
|
||||
#define HAVE_GETUNWIND 1
|
||||
|
||||
/* Define to 1 if you have the <ia64intrin.h> header file. */
|
||||
/* #undef HAVE_IA64INTRIN_H */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `uca' library (-luca). */
|
||||
/* #undef HAVE_LIBUCA */
|
||||
|
||||
/* Define to 1 if you have the <link.h> header file. */
|
||||
#define HAVE_LINK_H 1
|
||||
|
||||
/* Define if you have liblzma */
|
||||
#define HAVE_LZMA 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the `mincore' function. */
|
||||
#define HAVE_MINCORE 1
|
||||
|
||||
/* Define to 1 if you have the <signal.h> header file. */
|
||||
#define HAVE_SIGNAL_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if `dlpi_subs' is a member of `struct dl_phdr_info'. */
|
||||
/* #undef HAVE_STRUCT_DL_PHDR_INFO_DLPI_SUBS */
|
||||
|
||||
/* Define to 1 if the system has the type `struct elf_prstatus'. */
|
||||
/* #undef HAVE_STRUCT_ELF_PRSTATUS */
|
||||
|
||||
/* Define to 1 if the system has the type `struct prstatus'. */
|
||||
/* #undef HAVE_STRUCT_PRSTATUS */
|
||||
|
||||
/* Defined if __sync atomics are available */
|
||||
#define HAVE_SYNC_ATOMICS 1
|
||||
|
||||
/* Define to 1 if you have the <sys/elf.h> header file. */
|
||||
/* #undef HAVE_SYS_ELF_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/endian.h> header file. */
|
||||
#define HAVE_SYS_ENDIAN_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/link.h> header file. */
|
||||
/* #undef HAVE_SYS_LINK_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/procfs.h> header file. */
|
||||
/* #undef HAVE_SYS_PROCFS_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/ptrace.h> header file. */
|
||||
#define HAVE_SYS_PTRACE_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/uc_access.h> header file. */
|
||||
/* #undef HAVE_SYS_UC_ACCESS_H */
|
||||
|
||||
/* Define to 1 if you have the `ttrace' function. */
|
||||
/* #undef HAVE_TTRACE */
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Defined if __builtin_unreachable() is available */
|
||||
#define HAVE__BUILTIN_UNREACHABLE 1
|
||||
|
||||
/* Defined if __builtin___clear_cache() is available */
|
||||
#define HAVE__BUILTIN___CLEAR_CACHE 1
|
||||
|
||||
/* Define to 1 if __thread keyword is supported by the C compiler. */
|
||||
#define HAVE___THREAD 1
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
|
||||
/* #undef NO_MINUS_C_MINUS_O */
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "libunwind"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "libunwind-devel@nongnu.org"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "libunwind"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "libunwind 1.1"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "libunwind"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "1.1"
|
||||
|
||||
/* The size of `off_t', as computed by sizeof. */
|
||||
#define SIZEOF_OFF_T 4
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "1.1"
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
/* #undef inline */
|
||||
#endif
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
||||
@@ -1,128 +0,0 @@
|
||||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (c) 2003 Hewlett-Packard Development Company, L.P.
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#ifndef dwarf_eh_h
|
||||
#define dwarf_eh_h
|
||||
|
||||
#include "dwarf.h"
|
||||
|
||||
/* This header file defines the format of a DWARF exception-header
|
||||
section (.eh_frame_hdr, pointed to by program-header
|
||||
PT_GNU_EH_FRAME). The exception-header is self-describing in the
|
||||
sense that the format of the addresses contained in it is expressed
|
||||
as a one-byte type-descriptor called a "pointer-encoding" (PE).
|
||||
|
||||
The exception header encodes the address of the .eh_frame section
|
||||
and optionally contains a binary search table for the
|
||||
Frame Descriptor Entries (FDEs) in the .eh_frame. The contents of
|
||||
.eh_frame has the format described by the DWARF v3 standard
|
||||
(http://www.eagercon.com/dwarf/dwarf3std.htm), except that code
|
||||
addresses may be encoded in different ways. Also, .eh_frame has
|
||||
augmentations that allow encoding a language-specific data-area
|
||||
(LSDA) pointer and a pointer to a personality-routine.
|
||||
|
||||
Details:
|
||||
|
||||
The Common Information Entry (CIE) associated with an FDE may
|
||||
contain an augmentation string. Each character in this string has
|
||||
a specific meaning and either one or two associated operands. The
|
||||
operands are stored in an augmentation body which appears right
|
||||
after the "return_address_register" member and before the
|
||||
"initial_instructions" member. The operands appear in the order
|
||||
in which the characters appear in the string. For example, if the
|
||||
augmentation string is "zL", the operand for 'z' would be first in
|
||||
the augmentation body and the operand for 'L' would be second.
|
||||
The following characters are supported for the CIE augmentation
|
||||
string:
|
||||
|
||||
'z': The operand for this character is a uleb128 value that gives the
|
||||
length of the CIE augmentation body, not counting the length
|
||||
of the uleb128 operand itself. If present, this code must
|
||||
appear as the first character in the augmentation body.
|
||||
|
||||
'L': Indicates that the FDE's augmentation body contains an LSDA
|
||||
pointer. The operand for this character is a single byte
|
||||
that specifies the pointer-encoding (PE) that is used for
|
||||
the LSDA pointer.
|
||||
|
||||
'R': Indicates that the code-pointers (FDE members
|
||||
"initial_location" and "address_range" and the operand for
|
||||
DW_CFA_set_loc) in the FDE have a non-default encoding. The
|
||||
operand for this character is a single byte that specifies
|
||||
the pointer-encoding (PE) that is used for the
|
||||
code-pointers. Note: the "address_range" member is always
|
||||
encoded as an absolute value. Apart from that, the specified
|
||||
FDE pointer-encoding applies.
|
||||
|
||||
'P': Indicates the presence of a personality routine (handler).
|
||||
The first operand for this character specifies the
|
||||
pointer-encoding (PE) that is used for the second operand,
|
||||
which specifies the address of the personality routine.
|
||||
|
||||
If the augmentation string contains any other characters, the
|
||||
remainder of the augmentation string should be ignored.
|
||||
Furthermore, if the size of the augmentation body is unknown
|
||||
(i.e., 'z' is not the first character of the augmentation string),
|
||||
then the entire CIE as well all associated FDEs must be ignored.
|
||||
|
||||
A Frame Descriptor Entries (FDE) may contain an augmentation body
|
||||
which, if present, appears right after the "address_range" member
|
||||
and before the "instructions" member. The contents of this body
|
||||
is implicitly defined by the augmentation string of the associated
|
||||
CIE. The meaning of the characters in the CIE's augmentation
|
||||
string as far as FDEs are concerned is as follows:
|
||||
|
||||
'z': The first operand in the FDE's augmentation body specifies
|
||||
the total length of the augmentation body as a uleb128 (not
|
||||
counting the length of the uleb128 operand itself).
|
||||
|
||||
'L': The operand for this character is an LSDA pointer, encoded
|
||||
in the format specified by the corresponding operand in the
|
||||
CIE's augmentation body.
|
||||
|
||||
*/
|
||||
|
||||
#define DW_EH_VERSION 1 /* The version we're implementing */
|
||||
|
||||
struct dwarf_eh_frame_hdr
|
||||
{
|
||||
unsigned char version;
|
||||
unsigned char eh_frame_ptr_enc;
|
||||
unsigned char fde_count_enc;
|
||||
unsigned char table_enc;
|
||||
/* The rest of the header is variable-length and consists of the
|
||||
following members:
|
||||
|
||||
encoded_t eh_frame_ptr;
|
||||
encoded_t fde_count;
|
||||
struct
|
||||
{
|
||||
encoded_t start_ip; // first address covered by this FDE
|
||||
encoded_t fde_addr; // address of the FDE
|
||||
}
|
||||
binary_search_table[fde_count]; */
|
||||
};
|
||||
|
||||
#endif /* dwarf_eh_h */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user