diff options
author | Christopher Ferris | 2018-05-24 13:06:02 -0500 |
---|---|---|
committer | Gerrit Code Review | 2018-05-24 13:06:02 -0500 |
commit | a6a433a27d85aab489dc78b355bef60f4a6978ca (patch) | |
tree | 044f706880d7193f7d7541e6906e4dd1e1ca3585 | |
parent | 925b410c7f65f88f9483c68bfbdec64c31b92570 (diff) | |
parent | ca9a54b2c0381cc59598e8e560be9ad10b7c2e38 (diff) | |
download | platform-system-core-a6a433a27d85aab489dc78b355bef60f4a6978ca.tar.gz platform-system-core-a6a433a27d85aab489dc78b355bef60f4a6978ca.tar.xz platform-system-core-a6a433a27d85aab489dc78b355bef60f4a6978ca.zip |
Merge "Add a specialized LocalUnwinder object."
-rw-r--r-- | libunwindstack/Android.bp | 17 | ||||
-rw-r--r-- | libunwindstack/LocalUnwinder.cpp | 146 | ||||
-rw-r--r-- | libunwindstack/Maps.cpp | 79 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/LocalUnwinder.h | 86 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/Maps.h | 13 | ||||
-rw-r--r-- | libunwindstack/tests/LocalUnwinderTest.cpp | 207 | ||||
-rw-r--r-- | libunwindstack/tests/TestLocal.cpp | 39 | ||||
-rw-r--r-- | libunwindstack/tests/UnwindTest.cpp | 7 |
8 files changed, 589 insertions, 5 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index a6bf73073..435ed9462 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp | |||
@@ -62,6 +62,7 @@ cc_library { | |||
62 | "MapInfo.cpp", | 62 | "MapInfo.cpp", |
63 | "Maps.cpp", | 63 | "Maps.cpp", |
64 | "Memory.cpp", | 64 | "Memory.cpp", |
65 | "LocalUnwinder.cpp", | ||
65 | "Regs.cpp", | 66 | "Regs.cpp", |
66 | "RegsArm.cpp", | 67 | "RegsArm.cpp", |
67 | "RegsArm64.cpp", | 68 | "RegsArm64.cpp", |
@@ -125,6 +126,21 @@ cc_library { | |||
125 | //------------------------------------------------------------------------- | 126 | //------------------------------------------------------------------------- |
126 | // Unit Tests | 127 | // Unit Tests |
127 | //------------------------------------------------------------------------- | 128 | //------------------------------------------------------------------------- |
129 | cc_test_library { | ||
130 | name: "libunwindstack_local", | ||
131 | defaults: ["libunwindstack_flags"], | ||
132 | srcs: ["tests/TestLocal.cpp"], | ||
133 | |||
134 | cflags: [ | ||
135 | "-O0", | ||
136 | "-g", | ||
137 | ], | ||
138 | |||
139 | shared_libs: [ | ||
140 | "libunwindstack", | ||
141 | ], | ||
142 | } | ||
143 | |||
128 | cc_test { | 144 | cc_test { |
129 | name: "libunwindstack_test", | 145 | name: "libunwindstack_test", |
130 | defaults: ["libunwindstack_flags"], | 146 | defaults: ["libunwindstack_flags"], |
@@ -151,6 +167,7 @@ cc_test { | |||
151 | "tests/ElfTest.cpp", | 167 | "tests/ElfTest.cpp", |
152 | "tests/ElfTestUtils.cpp", | 168 | "tests/ElfTestUtils.cpp", |
153 | "tests/JitDebugTest.cpp", | 169 | "tests/JitDebugTest.cpp", |
170 | "tests/LocalUnwinderTest.cpp", | ||
154 | "tests/LogFake.cpp", | 171 | "tests/LogFake.cpp", |
155 | "tests/MapInfoGetElfTest.cpp", | 172 | "tests/MapInfoGetElfTest.cpp", |
156 | "tests/MapInfoGetLoadBiasTest.cpp", | 173 | "tests/MapInfoGetLoadBiasTest.cpp", |
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp new file mode 100644 index 000000000..952b332f0 --- /dev/null +++ b/libunwindstack/LocalUnwinder.cpp | |||
@@ -0,0 +1,146 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2018 The Android Open Source Project | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions | ||
7 | * are met: | ||
8 | * * Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * * Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in | ||
12 | * the documentation and/or other materials provided with the | ||
13 | * distribution. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
18 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
19 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | ||
21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | ||
22 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | ||
23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | ||
25 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
26 | * SUCH DAMAGE. | ||
27 | */ | ||
28 | |||
29 | #include <pthread.h> | ||
30 | #include <stdint.h> | ||
31 | |||
32 | #include <memory> | ||
33 | #include <string> | ||
34 | #include <vector> | ||
35 | |||
36 | #include <unwindstack/Elf.h> | ||
37 | #include <unwindstack/LocalUnwinder.h> | ||
38 | #include <unwindstack/MapInfo.h> | ||
39 | #include <unwindstack/Maps.h> | ||
40 | #include <unwindstack/Memory.h> | ||
41 | #include <unwindstack/Regs.h> | ||
42 | #include <unwindstack/RegsGetLocal.h> | ||
43 | |||
44 | namespace unwindstack { | ||
45 | |||
46 | bool LocalUnwinder::Init() { | ||
47 | pthread_rwlock_init(&maps_rwlock_, nullptr); | ||
48 | |||
49 | // Create the maps. | ||
50 | maps_.reset(new unwindstack::LocalUpdatableMaps()); | ||
51 | if (!maps_->Parse()) { | ||
52 | maps_.reset(); | ||
53 | return false; | ||
54 | } | ||
55 | |||
56 | process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid()); | ||
57 | |||
58 | return true; | ||
59 | } | ||
60 | |||
61 | bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) { | ||
62 | for (const std::string& skip_library : skip_libraries_) { | ||
63 | if (skip_library == map_name) { | ||
64 | return true; | ||
65 | } | ||
66 | } | ||
67 | return false; | ||
68 | } | ||
69 | |||
70 | MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) { | ||
71 | pthread_rwlock_rdlock(&maps_rwlock_); | ||
72 | MapInfo* map_info = maps_->Find(pc); | ||
73 | pthread_rwlock_unlock(&maps_rwlock_); | ||
74 | |||
75 | if (map_info == nullptr) { | ||
76 | pthread_rwlock_wrlock(&maps_rwlock_); | ||
77 | // This is guaranteed not to invalidate any previous MapInfo objects so | ||
78 | // we don't need to worry about any MapInfo* values already in use. | ||
79 | if (maps_->Reparse()) { | ||
80 | map_info = maps_->Find(pc); | ||
81 | } | ||
82 | pthread_rwlock_unlock(&maps_rwlock_); | ||
83 | } | ||
84 | |||
85 | return map_info; | ||
86 | } | ||
87 | |||
88 | bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) { | ||
89 | std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal()); | ||
90 | unwindstack::RegsGetLocal(regs.get()); | ||
91 | |||
92 | size_t num_frames = 0; | ||
93 | bool adjust_pc = false; | ||
94 | while (true) { | ||
95 | uint64_t cur_pc = regs->pc(); | ||
96 | uint64_t cur_sp = regs->sp(); | ||
97 | |||
98 | MapInfo* map_info = GetMapInfo(cur_pc); | ||
99 | if (map_info == nullptr) { | ||
100 | break; | ||
101 | } | ||
102 | |||
103 | Elf* elf = map_info->GetElf(process_memory_, true); | ||
104 | uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info); | ||
105 | uint64_t step_pc = rel_pc; | ||
106 | uint64_t pc_adjustment; | ||
107 | if (adjust_pc) { | ||
108 | pc_adjustment = regs->GetPcAdjustment(rel_pc, elf); | ||
109 | } else { | ||
110 | pc_adjustment = 0; | ||
111 | } | ||
112 | step_pc -= pc_adjustment; | ||
113 | // Skip any locations that are within this library. | ||
114 | if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) { | ||
115 | // Add frame information. | ||
116 | std::string func_name; | ||
117 | uint64_t func_offset; | ||
118 | if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) { | ||
119 | frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, | ||
120 | func_name, func_offset); | ||
121 | } else { | ||
122 | frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0); | ||
123 | } | ||
124 | num_frames++; | ||
125 | } | ||
126 | if (!elf->valid()) { | ||
127 | break; | ||
128 | } | ||
129 | if (frame_info->size() == max_frames) { | ||
130 | break; | ||
131 | } | ||
132 | |||
133 | adjust_pc = true; | ||
134 | bool finished; | ||
135 | if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) { | ||
136 | break; | ||
137 | } | ||
138 | // pc and sp are the same, terminate the unwind. | ||
139 | if (cur_pc == regs->pc() && cur_sp == regs->sp()) { | ||
140 | break; | ||
141 | } | ||
142 | } | ||
143 | return num_frames != 0; | ||
144 | } | ||
145 | |||
146 | } // namespace unwindstack | ||
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp index bb682ea69..e676a5a97 100644 --- a/libunwindstack/Maps.cpp +++ b/libunwindstack/Maps.cpp | |||
@@ -105,4 +105,83 @@ const std::string RemoteMaps::GetMapsFile() const { | |||
105 | return "/proc/" + std::to_string(pid_) + "/maps"; | 105 | return "/proc/" + std::to_string(pid_) + "/maps"; |
106 | } | 106 | } |
107 | 107 | ||
108 | const std::string LocalUpdatableMaps::GetMapsFile() const { | ||
109 | return "/proc/self/maps"; | ||
110 | } | ||
111 | |||
112 | bool LocalUpdatableMaps::Reparse() { | ||
113 | // New maps will be added at the end without deleting the old ones. | ||
114 | size_t last_map_idx = maps_.size(); | ||
115 | if (!Parse()) { | ||
116 | // Delete any maps added by the Parse call. | ||
117 | for (size_t i = last_map_idx; i < maps_.size(); i++) { | ||
118 | delete maps_[i]; | ||
119 | } | ||
120 | maps_.resize(last_map_idx); | ||
121 | return false; | ||
122 | } | ||
123 | |||
124 | size_t total_entries = maps_.size(); | ||
125 | size_t search_map_idx = 0; | ||
126 | for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) { | ||
127 | MapInfo* new_map_info = maps_[new_map_idx]; | ||
128 | uint64_t start = new_map_info->start; | ||
129 | uint64_t end = new_map_info->end; | ||
130 | uint64_t flags = new_map_info->flags; | ||
131 | std::string* name = &new_map_info->name; | ||
132 | for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) { | ||
133 | MapInfo* info = maps_[old_map_idx]; | ||
134 | if (start == info->start && end == info->end && flags == info->flags && *name == info->name) { | ||
135 | // No need to check | ||
136 | search_map_idx = old_map_idx + 1; | ||
137 | delete new_map_info; | ||
138 | maps_[new_map_idx] = nullptr; | ||
139 | total_entries--; | ||
140 | break; | ||
141 | } else if (info->start > start) { | ||
142 | // Stop, there isn't going to be a match. | ||
143 | search_map_idx = old_map_idx; | ||
144 | break; | ||
145 | } | ||
146 | |||
147 | // Never delete these maps, they may be in use. The assumption is | ||
148 | // that there will only every be a handfull of these so waiting | ||
149 | // to destroy them is not too expensive. | ||
150 | saved_maps_.push_back(info); | ||
151 | maps_[old_map_idx] = nullptr; | ||
152 | total_entries--; | ||
153 | } | ||
154 | if (search_map_idx >= last_map_idx) { | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | // Now move out any of the maps that never were found. | ||
160 | for (size_t i = search_map_idx; i < last_map_idx; i++) { | ||
161 | saved_maps_.push_back(maps_[i]); | ||
162 | maps_[i] = nullptr; | ||
163 | total_entries--; | ||
164 | } | ||
165 | |||
166 | // Sort all of the values such that the nullptrs wind up at the end, then | ||
167 | // resize them away. | ||
168 | std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) { | ||
169 | if (a == nullptr) { | ||
170 | return false; | ||
171 | } else if (b == nullptr) { | ||
172 | return true; | ||
173 | } | ||
174 | return a->start < b->start; | ||
175 | }); | ||
176 | maps_.resize(total_entries); | ||
177 | |||
178 | return true; | ||
179 | } | ||
180 | |||
181 | LocalUpdatableMaps::~LocalUpdatableMaps() { | ||
182 | for (auto map_info : saved_maps_) { | ||
183 | delete map_info; | ||
184 | } | ||
185 | } | ||
186 | |||
108 | } // namespace unwindstack | 187 | } // namespace unwindstack |
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h new file mode 100644 index 000000000..80bb53ec1 --- /dev/null +++ b/libunwindstack/include/unwindstack/LocalUnwinder.h | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2018 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #ifndef _LIBUNWINDSTACK_LOCAL_UNWINDER_H | ||
18 | #define _LIBUNWINDSTACK_LOCAL_UNWINDER_H | ||
19 | |||
20 | #include <pthread.h> | ||
21 | #include <stdint.h> | ||
22 | #include <sys/types.h> | ||
23 | |||
24 | #include <memory> | ||
25 | #include <string> | ||
26 | #include <vector> | ||
27 | |||
28 | #include <unwindstack/Error.h> | ||
29 | #include <unwindstack/Maps.h> | ||
30 | #include <unwindstack/Memory.h> | ||
31 | |||
32 | namespace unwindstack { | ||
33 | |||
34 | // Forward declarations. | ||
35 | class Elf; | ||
36 | struct MapInfo; | ||
37 | |||
38 | struct LocalFrameData { | ||
39 | LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name, | ||
40 | uint64_t function_offset) | ||
41 | : map_info(map_info), | ||
42 | pc(pc), | ||
43 | rel_pc(rel_pc), | ||
44 | function_name(function_name), | ||
45 | function_offset(function_offset) {} | ||
46 | |||
47 | MapInfo* map_info; | ||
48 | uint64_t pc; | ||
49 | uint64_t rel_pc; | ||
50 | std::string function_name; | ||
51 | uint64_t function_offset; | ||
52 | }; | ||
53 | |||
54 | // This is a specialized class that should only be used for doing local unwinds. | ||
55 | // The Unwind call can be made as multiple times on the same object, and it can | ||
56 | // be called by multiple threads at the same time. | ||
57 | // It is designed to be used in debugging circumstances to get a stack trace | ||
58 | // as fast as possible. | ||
59 | class LocalUnwinder { | ||
60 | public: | ||
61 | LocalUnwinder() = default; | ||
62 | LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {} | ||
63 | ~LocalUnwinder() = default; | ||
64 | |||
65 | bool Init(); | ||
66 | |||
67 | bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames); | ||
68 | |||
69 | bool ShouldSkipLibrary(const std::string& map_name); | ||
70 | |||
71 | MapInfo* GetMapInfo(uint64_t pc); | ||
72 | |||
73 | ErrorCode LastErrorCode() { return last_error_.code; } | ||
74 | uint64_t LastErrorAddress() { return last_error_.address; } | ||
75 | |||
76 | private: | ||
77 | pthread_rwlock_t maps_rwlock_; | ||
78 | std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr; | ||
79 | std::shared_ptr<Memory> process_memory_; | ||
80 | std::vector<std::string> skip_libraries_; | ||
81 | ErrorData last_error_; | ||
82 | }; | ||
83 | |||
84 | } // namespace unwindstack | ||
85 | |||
86 | #endif // _LIBUNWINDSTACK_LOCAL_UNWINDER_H | ||
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h index 74e5c4729..67fbed21a 100644 --- a/libunwindstack/include/unwindstack/Maps.h +++ b/libunwindstack/include/unwindstack/Maps.h | |||
@@ -87,6 +87,19 @@ class LocalMaps : public RemoteMaps { | |||
87 | virtual ~LocalMaps() = default; | 87 | virtual ~LocalMaps() = default; |
88 | }; | 88 | }; |
89 | 89 | ||
90 | class LocalUpdatableMaps : public Maps { | ||
91 | public: | ||
92 | LocalUpdatableMaps() : Maps() {} | ||
93 | virtual ~LocalUpdatableMaps(); | ||
94 | |||
95 | bool Reparse(); | ||
96 | |||
97 | const std::string GetMapsFile() const override; | ||
98 | |||
99 | private: | ||
100 | std::vector<MapInfo*> saved_maps_; | ||
101 | }; | ||
102 | |||
90 | class BufferMaps : public Maps { | 103 | class BufferMaps : public Maps { |
91 | public: | 104 | public: |
92 | BufferMaps(const char* buffer) : buffer_(buffer) {} | 105 | BufferMaps(const char* buffer) : buffer_(buffer) {} |
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp new file mode 100644 index 000000000..56a18cde2 --- /dev/null +++ b/libunwindstack/tests/LocalUnwinderTest.cpp | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2018 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include <dlfcn.h> | ||
18 | #include <inttypes.h> | ||
19 | #include <signal.h> | ||
20 | #include <stdint.h> | ||
21 | |||
22 | #include <memory> | ||
23 | #include <string> | ||
24 | #include <vector> | ||
25 | |||
26 | #include <gtest/gtest.h> | ||
27 | |||
28 | #include <android-base/stringprintf.h> | ||
29 | |||
30 | #include <unwindstack/LocalUnwinder.h> | ||
31 | |||
32 | namespace unwindstack { | ||
33 | |||
34 | static std::vector<LocalFrameData>* g_frame_info; | ||
35 | static LocalUnwinder* g_unwinder; | ||
36 | |||
37 | extern "C" void SignalLocalInnerFunction() { | ||
38 | g_unwinder->Unwind(g_frame_info, 256); | ||
39 | } | ||
40 | |||
41 | extern "C" void SignalLocalMiddleFunction() { | ||
42 | SignalLocalInnerFunction(); | ||
43 | } | ||
44 | |||
45 | extern "C" void SignalLocalOuterFunction() { | ||
46 | SignalLocalMiddleFunction(); | ||
47 | } | ||
48 | |||
49 | static void SignalLocalCallerHandler(int, siginfo_t*, void*) { | ||
50 | SignalLocalOuterFunction(); | ||
51 | } | ||
52 | |||
53 | static std::string ErrorMsg(const std::vector<const char*>& function_names, | ||
54 | const std::vector<LocalFrameData>& frame_info) { | ||
55 | std::string unwind; | ||
56 | size_t i = 0; | ||
57 | for (const auto& frame : frame_info) { | ||
58 | unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++, | ||
59 | frame.pc, frame.rel_pc); | ||
60 | if (frame.map_info != nullptr) { | ||
61 | if (!frame.map_info->name.empty()) { | ||
62 | unwind += " " + frame.map_info->name; | ||
63 | } else { | ||
64 | unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start, | ||
65 | frame.map_info->end); | ||
66 | } | ||
67 | if (frame.map_info->offset != 0) { | ||
68 | unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset); | ||
69 | } | ||
70 | } | ||
71 | if (!frame.function_name.empty()) { | ||
72 | unwind += " " + frame.function_name; | ||
73 | if (frame.function_offset != 0) { | ||
74 | unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset); | ||
75 | } | ||
76 | } | ||
77 | unwind += '\n'; | ||
78 | } | ||
79 | |||
80 | return std::string( | ||
81 | "Unwind completed without finding all frames\n" | ||
82 | " Looking for function: ") + | ||
83 | function_names.front() + "\n" + "Unwind data:\n" + unwind; | ||
84 | } | ||
85 | |||
86 | // This test assumes that this code is compiled with optimizations turned | ||
87 | // off. If this doesn't happen, then all of the calls will be optimized | ||
88 | // away. | ||
89 | extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { | ||
90 | std::vector<LocalFrameData> frame_info; | ||
91 | g_frame_info = &frame_info; | ||
92 | g_unwinder = unwinder; | ||
93 | std::vector<const char*> expected_function_names; | ||
94 | |||
95 | if (unwind_through_signal) { | ||
96 | struct sigaction act, oldact; | ||
97 | memset(&act, 0, sizeof(act)); | ||
98 | act.sa_sigaction = SignalLocalCallerHandler; | ||
99 | act.sa_flags = SA_RESTART | SA_ONSTACK; | ||
100 | ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact)); | ||
101 | |||
102 | raise(SIGUSR1); | ||
103 | |||
104 | ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr)); | ||
105 | |||
106 | expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", | ||
107 | "LocalInnerFunction", "SignalLocalOuterFunction", | ||
108 | "SignalLocalMiddleFunction", "SignalLocalInnerFunction"}; | ||
109 | } else { | ||
110 | ASSERT_TRUE(unwinder->Unwind(&frame_info, 256)); | ||
111 | |||
112 | expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"}; | ||
113 | } | ||
114 | |||
115 | for (auto& frame : frame_info) { | ||
116 | if (frame.function_name == expected_function_names.back()) { | ||
117 | expected_function_names.pop_back(); | ||
118 | if (expected_function_names.empty()) { | ||
119 | break; | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info); | ||
125 | } | ||
126 | |||
127 | extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { | ||
128 | LocalInnerFunction(unwinder, unwind_through_signal); | ||
129 | } | ||
130 | |||
131 | extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { | ||
132 | LocalMiddleFunction(unwinder, unwind_through_signal); | ||
133 | } | ||
134 | |||
135 | class LocalUnwinderTest : public ::testing::Test { | ||
136 | protected: | ||
137 | void SetUp() override { | ||
138 | unwinder_.reset(new LocalUnwinder); | ||
139 | ASSERT_TRUE(unwinder_->Init()); | ||
140 | } | ||
141 | |||
142 | std::unique_ptr<LocalUnwinder> unwinder_; | ||
143 | }; | ||
144 | |||
145 | TEST_F(LocalUnwinderTest, local) { | ||
146 | LocalOuterFunction(unwinder_.get(), false); | ||
147 | } | ||
148 | |||
149 | TEST_F(LocalUnwinderTest, local_signal) { | ||
150 | LocalOuterFunction(unwinder_.get(), true); | ||
151 | } | ||
152 | |||
153 | TEST_F(LocalUnwinderTest, local_multiple) { | ||
154 | ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); | ||
155 | |||
156 | ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true)); | ||
157 | |||
158 | ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); | ||
159 | |||
160 | ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true)); | ||
161 | } | ||
162 | |||
163 | // This test verifies that doing an unwind before and after a dlopen | ||
164 | // works. It's verifying that the maps read during the first unwind | ||
165 | // do not cause a problem when doing the unwind using the code in | ||
166 | // the dlopen'd code. | ||
167 | TEST_F(LocalUnwinderTest, unwind_after_dlopen) { | ||
168 | // Prime the maps data. | ||
169 | ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); | ||
170 | |||
171 | std::string testlib(testing::internal::GetArgvs()[0]); | ||
172 | auto const value = testlib.find_last_of('/'); | ||
173 | if (value == std::string::npos) { | ||
174 | testlib = "../"; | ||
175 | } else { | ||
176 | testlib = testlib.substr(0, value + 1) + "../"; | ||
177 | } | ||
178 | testlib += "libunwindstack_local.so"; | ||
179 | |||
180 | void* handle = dlopen(testlib.c_str(), RTLD_NOW); | ||
181 | ASSERT_TRUE(handle != nullptr); | ||
182 | |||
183 | void (*unwind_function)(void*, void*) = | ||
184 | reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1")); | ||
185 | ASSERT_TRUE(unwind_function != nullptr); | ||
186 | |||
187 | std::vector<LocalFrameData> frame_info; | ||
188 | unwind_function(unwinder_.get(), &frame_info); | ||
189 | |||
190 | ASSERT_EQ(0, dlclose(handle)); | ||
191 | |||
192 | std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2", | ||
193 | "TestlibLevel3", "TestlibLevel4"}; | ||
194 | |||
195 | for (auto& frame : frame_info) { | ||
196 | if (frame.function_name == expected_function_names.back()) { | ||
197 | expected_function_names.pop_back(); | ||
198 | if (expected_function_names.empty()) { | ||
199 | break; | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | |||
204 | ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info); | ||
205 | } | ||
206 | |||
207 | } // namespace unwindstack | ||
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp new file mode 100644 index 000000000..fa0baff36 --- /dev/null +++ b/libunwindstack/tests/TestLocal.cpp | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2018 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include <unwindstack/LocalUnwinder.h> | ||
18 | |||
19 | #include <vector> | ||
20 | |||
21 | extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) { | ||
22 | unwindstack::LocalUnwinder* unwinder = | ||
23 | reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data); | ||
24 | std::vector<unwindstack::LocalFrameData>* frame_info = | ||
25 | reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data); | ||
26 | unwinder->Unwind(frame_info, 256); | ||
27 | } | ||
28 | |||
29 | extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) { | ||
30 | TestlibLevel4(unwinder_data, frame_data); | ||
31 | } | ||
32 | |||
33 | extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) { | ||
34 | TestlibLevel3(unwinder_data, frame_data); | ||
35 | } | ||
36 | |||
37 | extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) { | ||
38 | TestlibLevel2(unwinder_data, frame_data); | ||
39 | } | ||
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp index 242cc6a6f..83695bbcd 100644 --- a/libunwindstack/tests/UnwindTest.cpp +++ b/libunwindstack/tests/UnwindTest.cpp | |||
@@ -106,15 +106,12 @@ static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs, | |||
106 | Unwinder unwinder(512, maps, regs, process_memory); | 106 | Unwinder unwinder(512, maps, regs, process_memory); |
107 | unwinder.Unwind(); | 107 | unwinder.Unwind(); |
108 | 108 | ||
109 | std::string expected_function = expected_function_names.back(); | ||
110 | expected_function_names.pop_back(); | ||
111 | for (auto& frame : unwinder.frames()) { | 109 | for (auto& frame : unwinder.frames()) { |
112 | if (frame.function_name == expected_function) { | 110 | if (frame.function_name == expected_function_names.back()) { |
111 | expected_function_names.pop_back(); | ||
113 | if (expected_function_names.empty()) { | 112 | if (expected_function_names.empty()) { |
114 | break; | 113 | break; |
115 | } | 114 | } |
116 | expected_function = expected_function_names.back(); | ||
117 | expected_function_names.pop_back(); | ||
118 | } | 115 | } |
119 | } | 116 | } |
120 | 117 | ||