blob: d35188d04897ea3aa622aec4316b9224c4dae7af [file] [log] [blame]
Andreas Gampe5dd44d02016-08-02 17:20:03 -07001/*
2 * Copyright (C) 2016 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 "native_stack_dump.h"
18
Yi Kongc57c6802018-10-29 14:28:56 -070019#include <memory>
Andreas Gampe5dd44d02016-08-02 17:20:03 -070020#include <ostream>
David Srbeckyd4ffc912022-09-26 18:11:38 +010021#include <string_view>
Andreas Gampe5dd44d02016-08-02 17:20:03 -070022
23#include <stdio.h>
24
25#include "art_method.h"
26
27// For DumpNativeStack.
Christopher Ferrisf44070e2022-03-26 09:54:32 -070028#include <unwindstack/AndroidUnwinder.h>
Andreas Gampe5dd44d02016-08-02 17:20:03 -070029
30#if defined(__linux__)
31
32#include <memory>
33#include <vector>
34
35#include <linux/unistd.h>
Christopher Ferris453e0e52018-03-06 14:02:55 -080036#include <poll.h>
Andreas Gampe5dd44d02016-08-02 17:20:03 -070037#include <signal.h>
38#include <stdlib.h>
39#include <sys/time.h>
40#include <sys/types.h>
41
Andreas Gampe43e72432019-05-14 16:15:24 -070042#include "android-base/file.h"
Andreas Gampe46ee31b2016-12-14 10:11:49 -080043#include "android-base/stringprintf.h"
Christopher Ferrisb1f23f92018-03-07 14:10:49 -080044#include "android-base/strings.h"
Andreas Gampe46ee31b2016-12-14 10:11:49 -080045
Andreas Gampe5dd44d02016-08-02 17:20:03 -070046#include "arch/instruction_set.h"
Andreas Gampe39b378c2017-12-07 15:44:13 -080047#include "base/aborting.h"
David Srbeckyeea5fd32019-02-13 17:24:17 +000048#include "base/bit_utils.h"
David Sehr891a50e2017-10-27 17:01:07 -070049#include "base/file_utils.h"
Andreas Gampe5dd44d02016-08-02 17:20:03 -070050#include "base/memory_tool.h"
51#include "base/mutex.h"
David Sehrc431b9d2018-03-02 12:01:51 -080052#include "base/os.h"
Andreas Gampefcccbaf2016-08-02 17:20:03 -070053#include "base/unix_file/fd_file.h"
David Sehrc431b9d2018-03-02 12:01:51 -080054#include "base/utils.h"
David Srbeckybb720732019-01-29 16:54:47 +000055#include "class_linker.h"
David Srbeckyeea5fd32019-02-13 17:24:17 +000056#include "entrypoints/runtime_asm_entrypoints.h"
Andreas Gampe5dd44d02016-08-02 17:20:03 -070057#include "oat_quick_method_header.h"
David Srbeckybb720732019-01-29 16:54:47 +000058#include "runtime.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070059#include "thread-current-inl.h"
Andreas Gampe5dd44d02016-08-02 17:20:03 -070060
61#endif
62
63namespace art {
64
65#if defined(__linux__)
66
Andreas Gampe46ee31b2016-12-14 10:11:49 -080067using android::base::StringPrintf;
68
Andreas Gampe5dd44d02016-08-02 17:20:03 -070069static constexpr bool kUseAddr2line = !kIsTargetBuild;
70
David Srbecky87da30e2019-01-30 15:51:23 +000071std::string FindAddr2line() {
David Srbeckya974baa2022-06-08 14:58:39 +010072#if !defined(ART_TARGET) && !defined(ART_CLANG_PATH)
David Srbeckyc4be8812022-05-03 17:25:21 +010073 #error "ART_CLANG_PATH must be defined on host build"
74#endif
75#if defined(ART_CLANG_PATH)
David Srbecky7400a542020-07-09 13:40:57 +010076 const char* env_value = getenv("ANDROID_BUILD_TOP");
David Srbeckyc2042a12023-02-23 17:09:32 +000077 std::string_view top(env_value != nullptr ? env_value : ".");
78 return std::string(top) + "/" + ART_CLANG_PATH + "/bin/llvm-addr2line";
79#else
David Srbeckyc4be8812022-05-03 17:25:21 +010080 return std::string("llvm-addr2line");
David Srbeckyc2042a12023-02-23 17:09:32 +000081#endif
David Srbecky87da30e2019-01-30 15:51:23 +000082}
83
Andreas Gampe5dd44d02016-08-02 17:20:03 -070084ALWAYS_INLINE
Andreas Gampefcccbaf2016-08-02 17:20:03 -070085static inline void WritePrefix(std::ostream& os, const char* prefix, bool odd) {
Andreas Gampe5dd44d02016-08-02 17:20:03 -070086 if (prefix != nullptr) {
Andreas Gampefcccbaf2016-08-02 17:20:03 -070087 os << prefix;
Andreas Gampe5dd44d02016-08-02 17:20:03 -070088 }
Andreas Gampefcccbaf2016-08-02 17:20:03 -070089 os << " ";
Andreas Gampe5dd44d02016-08-02 17:20:03 -070090 if (!odd) {
Andreas Gampefcccbaf2016-08-02 17:20:03 -070091 os << " ";
Andreas Gampe5dd44d02016-08-02 17:20:03 -070092 }
93}
94
Andreas Gampefcccbaf2016-08-02 17:20:03 -070095// The state of an open pipe to addr2line. In "server" mode, addr2line takes input on stdin
96// and prints the result to stdout. This struct keeps the state of the open connection.
97struct Addr2linePipe {
98 Addr2linePipe(int in_fd, int out_fd, const std::string& file_name, pid_t pid)
99 : in(in_fd, false), out(out_fd, false), file(file_name), child_pid(pid), odd(true) {}
100
101 ~Addr2linePipe() {
102 kill(child_pid, SIGKILL);
103 }
104
105 File in; // The file descriptor that is connected to the output of addr2line.
106 File out; // The file descriptor that is connected to the input of addr2line.
107
108 const std::string file; // The file addr2line is working on, so that we know when to close
109 // and restart.
110 const pid_t child_pid; // The pid of the child, which we should kill when we're done.
111 bool odd; // Print state for indentation of lines.
112};
113
114static std::unique_ptr<Addr2linePipe> Connect(const std::string& name, const char* args[]) {
115 int caller_to_addr2line[2];
116 int addr2line_to_caller[2];
117
118 if (pipe(caller_to_addr2line) == -1) {
119 return nullptr;
120 }
121 if (pipe(addr2line_to_caller) == -1) {
122 close(caller_to_addr2line[0]);
123 close(caller_to_addr2line[1]);
124 return nullptr;
125 }
126
127 pid_t pid = fork();
128 if (pid == -1) {
129 close(caller_to_addr2line[0]);
130 close(caller_to_addr2line[1]);
Calin Juravle0ed6c802017-03-27 18:12:05 -0700131 close(addr2line_to_caller[0]);
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700132 close(addr2line_to_caller[1]);
133 return nullptr;
134 }
135
136 if (pid == 0) {
137 dup2(caller_to_addr2line[0], STDIN_FILENO);
138 dup2(addr2line_to_caller[1], STDOUT_FILENO);
139
140 close(caller_to_addr2line[0]);
141 close(caller_to_addr2line[1]);
142 close(addr2line_to_caller[0]);
143 close(addr2line_to_caller[1]);
144
145 execv(args[0], const_cast<char* const*>(args));
146 exit(1);
147 } else {
148 close(caller_to_addr2line[0]);
149 close(addr2line_to_caller[1]);
Yi Kongc57c6802018-10-29 14:28:56 -0700150 return std::make_unique<Addr2linePipe>(addr2line_to_caller[0],
151 caller_to_addr2line[1],
152 name,
153 pid);
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700154 }
155}
156
157static void Drain(size_t expected,
158 const char* prefix,
159 std::unique_ptr<Addr2linePipe>* pipe /* inout */,
160 std::ostream& os) {
161 DCHECK(pipe != nullptr);
162 DCHECK(pipe->get() != nullptr);
163 int in = pipe->get()->in.Fd();
164 DCHECK_GE(in, 0);
165
166 bool prefix_written = false;
167
168 for (;;) {
Christopher Ferris453e0e52018-03-06 14:02:55 -0800169 constexpr uint32_t kWaitTimeExpectedMilli = 500;
170 constexpr uint32_t kWaitTimeUnexpectedMilli = 50;
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700171
Christopher Ferris453e0e52018-03-06 14:02:55 -0800172 int timeout = expected > 0 ? kWaitTimeExpectedMilli : kWaitTimeUnexpectedMilli;
173 struct pollfd read_fd{in, POLLIN, 0};
174 int retval = TEMP_FAILURE_RETRY(poll(&read_fd, 1, timeout));
175 if (retval == -1) {
176 // An error occurred.
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700177 pipe->reset();
178 return;
179 }
180
181 if (retval == 0) {
182 // Timeout.
183 return;
184 }
185
Christopher Ferris453e0e52018-03-06 14:02:55 -0800186 if (!(read_fd.revents & POLLIN)) {
187 // addr2line call exited.
188 pipe->reset();
189 return;
190 }
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700191
192 constexpr size_t kMaxBuffer = 128; // Relatively small buffer. Should be OK as we're on an
193 // alt stack, but just to be sure...
194 char buffer[kMaxBuffer];
195 memset(buffer, 0, kMaxBuffer);
196 int bytes_read = TEMP_FAILURE_RETRY(read(in, buffer, kMaxBuffer - 1));
Christopher Ferris453e0e52018-03-06 14:02:55 -0800197 if (bytes_read <= 0) {
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700198 // This should not really happen...
199 pipe->reset();
200 return;
201 }
Christopher Ferris453e0e52018-03-06 14:02:55 -0800202 buffer[bytes_read] = '\0';
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700203
204 char* tmp = buffer;
205 while (*tmp != 0) {
206 if (!prefix_written) {
207 WritePrefix(os, prefix, (*pipe)->odd);
208 prefix_written = true;
209 }
210 char* new_line = strchr(tmp, '\n');
211 if (new_line == nullptr) {
212 os << tmp;
213
214 break;
215 } else {
216 char saved = *(new_line + 1);
217 *(new_line + 1) = 0;
218 os << tmp;
219 *(new_line + 1) = saved;
220
221 tmp = new_line + 1;
222 prefix_written = false;
223 (*pipe)->odd = !(*pipe)->odd;
224
225 if (expected > 0) {
226 expected--;
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700227 }
228 }
229 }
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700230 }
231}
232
233static void Addr2line(const std::string& map_src,
234 uintptr_t offset,
235 std::ostream& os,
236 const char* prefix,
237 std::unique_ptr<Addr2linePipe>* pipe /* inout */) {
Orion Hodson3e9d7ae2019-11-18 15:21:51 +0000238 std::array<const char*, 3> kIgnoreSuffixes{ ".dex", ".jar", ".vdex" };
239 for (const char* ignore_suffix : kIgnoreSuffixes) {
240 if (android::base::EndsWith(map_src, ignore_suffix)) {
241 // Ignore file names that do not have map information addr2line can consume. e.g. vdex
242 // files are special frames injected for the interpreter so they don't have any line
243 // number information available.
244 return;
245 }
246 }
247 if (map_src == "[vdso]") {
Christopher Ferrisb1f23f92018-03-07 14:10:49 -0800248 // addr2line will not work on the vdso.
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700249 return;
250 }
251
252 if (*pipe == nullptr || (*pipe)->file != map_src) {
253 if (*pipe != nullptr) {
254 Drain(0, prefix, pipe, os);
255 }
256 pipe->reset(); // Close early.
257
David Srbecky87da30e2019-01-30 15:51:23 +0000258 std::string addr2linePath = FindAddr2line();
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700259 const char* args[7] = {
David Srbecky87da30e2019-01-30 15:51:23 +0000260 addr2linePath.c_str(),
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700261 "--functions",
262 "--inlines",
263 "--demangle",
264 "-e",
265 map_src.c_str(),
266 nullptr
267 };
268 *pipe = Connect(map_src, args);
269 }
270
271 Addr2linePipe* pipe_ptr = pipe->get();
272 if (pipe_ptr == nullptr) {
273 // Failed...
274 return;
275 }
276
277 // Send the offset.
David Srbecky7400a542020-07-09 13:40:57 +0100278 const std::string hex_offset = StringPrintf("0x%zx\n", offset);
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700279
280 if (!pipe_ptr->out.WriteFully(hex_offset.data(), hex_offset.length())) {
281 // Error. :-(
282 pipe->reset();
283 return;
284 }
285
286 // Now drain (expecting two lines).
287 Drain(2U, prefix, pipe, os);
288}
289
Andreas Gampeca620d72016-11-08 08:09:33 -0800290static bool RunCommand(const std::string& cmd) {
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700291 FILE* stream = popen(cmd.c_str(), "r");
292 if (stream) {
David Srbeckyf3720542021-11-30 16:40:19 +0000293 // Consume the stdout until we encounter EOF when the tool exits.
294 // Otherwise the tool would complain to stderr when the stream is closed.
295 char buffer[64];
296 while (fread(buffer, 1, sizeof(buffer), stream) == sizeof(buffer)) {}
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700297 pclose(stream);
298 return true;
299 } else {
300 return false;
301 }
302}
303
David Srbecky6940a812022-09-29 14:40:10 +0100304// Remove method parameters by finding matching top-level parenthesis and removing them.
305// Since functions can be defined inside functions, this can remove multiple substrings.
306std::string StripParameters(std::string name) {
307 size_t end = name.size();
David Srbeckyd4ffc912022-09-26 18:11:38 +0100308 int nesting = 0;
309 for (ssize_t i = name.size() - 1; i > 0; i--) {
David Srbecky6940a812022-09-29 14:40:10 +0100310 if (name[i] == ')' && nesting++ == 0) {
311 end = i + 1;
312 }
313 if (name[i] == '(' && --nesting == 0) {
314 name = name.erase(i, end - i);
David Srbeckyd4ffc912022-09-26 18:11:38 +0100315 }
316 }
317 return name;
318}
319
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700320void DumpNativeStack(std::ostream& os,
321 pid_t tid,
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700322 const char* prefix,
323 ArtMethod* current_method,
324 void* ucontext_ptr,
325 bool skip_frames) {
326 unwindstack::AndroidLocalUnwinder unwinder;
327 DumpNativeStack(os, unwinder, tid, prefix, current_method, ucontext_ptr, skip_frames);
328}
329
330void DumpNativeStack(std::ostream& os,
331 unwindstack::AndroidLocalUnwinder& unwinder,
332 pid_t tid,
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700333 const char* prefix,
334 ArtMethod* current_method,
Christopher Ferrisb2749312018-03-23 13:03:45 -0700335 void* ucontext_ptr,
336 bool skip_frames) {
Roland Levillain05e34f42018-05-24 13:19:05 +0000337 // Historical note: This was disabled when running under Valgrind (b/18119146).
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700338
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700339 unwindstack::AndroidUnwinderData data(!skip_frames /*show_all_frames*/);
340 bool unwind_ret;
341 if (ucontext_ptr != nullptr) {
342 unwind_ret = unwinder.Unwind(ucontext_ptr, data);
343 } else {
344 unwind_ret = unwinder.Unwind(tid, data);
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700345 }
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700346 if (!unwind_ret) {
347 os << prefix << "(Unwind failed for thread " << tid << ": "
348 << data.GetErrorString() << ")" << std::endl;
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700349 return;
350 }
351
352 // Check whether we have and should use addr2line.
353 bool use_addr2line;
354 if (kUseAddr2line) {
355 // Try to run it to see whether we have it. Push an argument so that it doesn't assume a.out
356 // and print to stderr.
David Srbecky87da30e2019-01-30 15:51:23 +0000357 use_addr2line = (gAborting > 0) && RunCommand(FindAddr2line() + " -h");
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700358 } else {
359 use_addr2line = false;
360 }
361
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700362 std::unique_ptr<Addr2linePipe> addr2line_state;
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700363 data.DemangleFunctionNames();
Nicolas Geoffray8008d6b2023-01-05 15:03:07 +0000364 bool holds_mutator_lock = Locks::mutator_lock_->IsSharedHeld(Thread::Current());
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700365 for (const unwindstack::FrameData& frame : data.frames) {
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700366 // We produce output like this:
367 // ] #00 pc 000075bb8 /system/lib/libc.so (unwind_backtrace_thread+536)
368 // In order for parsing tools to continue to function, the stack dump
369 // format must at least adhere to this format:
370 // #XX pc <RELATIVE_ADDR> <FULL_PATH_TO_SHARED_LIBRARY> ...
371 // The parsers require a single space before and after pc, and two spaces
372 // after the <RELATIVE_ADDR>. There can be any prefix data before the
373 // #XX. <RELATIVE_ADDR> has to be a hex number but with no 0x prefix.
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700374 os << prefix << StringPrintf("#%02zu pc ", frame.num);
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700375 bool try_addr2line = false;
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700376 if (frame.map_info == nullptr) {
David Srbecky0d847082022-09-26 16:40:56 +0100377 os << StringPrintf("%08" PRIx64 " ???", frame.pc);
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700378 } else {
David Srbecky0d847082022-09-26 16:40:56 +0100379 os << StringPrintf("%08" PRIx64 " ", frame.rel_pc);
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700380 const std::shared_ptr<unwindstack::MapInfo>& map_info = frame.map_info;
381 if (map_info->name().empty()) {
382 os << StringPrintf("<anonymous:%" PRIx64 ">", map_info->start());
Christopher Ferris8bd7d1b2018-01-08 11:12:40 -0800383 } else {
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700384 os << map_info->name().c_str();
Christopher Ferris8bd7d1b2018-01-08 11:12:40 -0800385 }
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700386 if (map_info->elf_start_offset() != 0) {
387 os << StringPrintf(" (offset %" PRIx64 ")", map_info->elf_start_offset());
Christopher Ferris53ef6a62018-02-09 23:13:27 -0800388 }
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700389 os << " (";
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700390 if (!frame.function_name.empty()) {
David Srbeckyd4ffc912022-09-26 18:11:38 +0100391 // Remove parameters from the printed function name to improve signal/noise in the logs.
392 // Also, ANRs are often trimmed, so printing less means we get more useful data out.
393 // We can still symbolize the function based on the PC and build-id (including inlining).
394 os << StripParameters(frame.function_name.c_str());
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700395 if (frame.function_offset != 0) {
396 os << "+" << frame.function_offset;
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700397 }
Christopher Ferris8bd7d1b2018-01-08 11:12:40 -0800398 // Functions found using the gdb jit interface will be in an empty
399 // map that cannot be found using addr2line.
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700400 if (!map_info->name().empty()) {
Christopher Ferris8bd7d1b2018-01-08 11:12:40 -0800401 try_addr2line = true;
402 }
Nicolas Geoffray8008d6b2023-01-05 15:03:07 +0000403 } else if (current_method != nullptr && holds_mutator_lock) {
404 const OatQuickMethodHeader* header = current_method->GetOatQuickMethodHeader(frame.pc);
405 if (header != nullptr) {
406 const void* start_of_code = header->GetCode();
407 os << current_method->JniLongName() << "+"
408 << (frame.pc - reinterpret_cast<uint64_t>(start_of_code));
409 } else {
410 os << "???";
411 }
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700412 } else {
413 os << "???";
414 }
415 os << ")";
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700416 std::string build_id = map_info->GetPrintableBuildID();
David Srbecky7cd509c2021-10-28 16:27:16 +0100417 if (!build_id.empty()) {
418 os << " (BuildId: " << build_id << ")";
419 }
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700420 }
Andreas Gampeef295362016-10-11 20:04:11 -0700421 os << std::endl;
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700422 if (try_addr2line && use_addr2line) {
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700423 // Guaranteed that map_info is not nullptr and name is non-empty.
424 Addr2line(frame.map_info->name(), frame.rel_pc, os, prefix, &addr2line_state);
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700425 }
426 }
Andreas Gampefcccbaf2016-08-02 17:20:03 -0700427
428 if (addr2line_state != nullptr) {
429 Drain(0, prefix, &addr2line_state, os);
430 }
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700431}
432
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700433#elif defined(__APPLE__)
434
435void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED,
436 pid_t tid ATTRIBUTE_UNUSED,
Christopher Ferrisf44070e2022-03-26 09:54:32 -0700437 const char* prefix ATTRIBUTE_UNUSED,
438 ArtMethod* current_method ATTRIBUTE_UNUSED,
439 void* ucontext_ptr ATTRIBUTE_UNUSED,
440 bool skip_frames ATTRIBUTE_UNUSED) {
441}
442
443void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED,
444 unwindstack::AndroidLocalUnwinder& existing_map ATTRIBUTE_UNUSED,
445 pid_t tid ATTRIBUTE_UNUSED,
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700446 const char* prefix ATTRIBUTE_UNUSED,
447 ArtMethod* current_method ATTRIBUTE_UNUSED,
Christopher Ferrisa0b25272018-03-27 17:04:44 -0700448 void* ucontext_ptr ATTRIBUTE_UNUSED,
449 bool skip_frames ATTRIBUTE_UNUSED) {
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700450}
451
Andreas Gampe5dd44d02016-08-02 17:20:03 -0700452#else
453#error "Unsupported architecture for native stack dumps."
454#endif
455
456} // namespace art