# Copyright 2018 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.

import collections
import contextlib
import logging
import os
import re
import shutil
import tempfile
import unittest

from pylib.symbols import apk_native_libs_unittest
from pylib.symbols import mock_addr2line
from pylib.symbols import symbol_utils

_MOCK_ELF_DATA = apk_native_libs_unittest.MOCK_ELF_DATA

_MOCK_A2L_PATH = os.path.join(os.path.dirname(mock_addr2line.__file__),
                              'mock_addr2line')


# pylint: disable=line-too-long

# list of (start_offset, end_offset, size, libpath) tuples corresponding
# to the content of base.apk. This was taken from an x86 ChromeModern.apk
# component build.
_TEST_APK_LIBS = [
  (0x01331000, 0x013696bc, 0x000386bc, 'libaccessibility.cr.so'),
  (0x0136a000, 0x013779c4, 0x0000d9c4, 'libanimation.cr.so'),
  (0x01378000, 0x0137f7e8, 0x000077e8, 'libapdu.cr.so'),
  (0x01380000, 0x0155ccc8, 0x001dccc8, 'libbase.cr.so'),
  (0x0155d000, 0x015ab98c, 0x0004e98c, 'libbase_i18n.cr.so'),
  (0x015ac000, 0x015dff4c, 0x00033f4c, 'libbindings.cr.so'),
  (0x015e0000, 0x015f5a54, 0x00015a54, 'libbindings_base.cr.so'),
  (0x0160e000, 0x01731960, 0x00123960, 'libblink_common.cr.so'),
  (0x01732000, 0x0174ce54, 0x0001ae54, 'libblink_controller.cr.so'),
  (0x0174d000, 0x0318c528, 0x01a3f528, 'libblink_core.cr.so'),
  (0x0318d000, 0x03191700, 0x00004700, 'libblink_mojom_broadcastchannel_bindings_shared.cr.so'),
  (0x03192000, 0x03cd7918, 0x00b45918, 'libblink_modules.cr.so'),
  (0x03cd8000, 0x03d137d0, 0x0003b7d0, 'libblink_mojo_bindings_shared.cr.so'),
  (0x03d14000, 0x03d2670c, 0x0001270c, 'libblink_offscreen_canvas_mojo_bindings_shared.cr.so'),
  (0x03d27000, 0x046c7054, 0x009a0054, 'libblink_platform.cr.so'),
  (0x046c8000, 0x0473fbfc, 0x00077bfc, 'libbluetooth.cr.so'),
  (0x04740000, 0x04878f40, 0x00138f40, 'libboringssl.cr.so'),
  (0x04879000, 0x0498466c, 0x0010b66c, 'libc++_shared.so'),
  (0x04985000, 0x0498d93c, 0x0000893c, 'libcaptive_portal.cr.so'),
  (0x0498e000, 0x049947cc, 0x000067cc, 'libcapture_base.cr.so'),
  (0x04995000, 0x04b39f18, 0x001a4f18, 'libcapture_lib.cr.so'),
  (0x04b3a000, 0x04b488ec, 0x0000e8ec, 'libcbor.cr.so'),
  (0x04b49000, 0x04e9ea5c, 0x00355a5c, 'libcc.cr.so'),
  (0x04e9f000, 0x04ed6404, 0x00037404, 'libcc_animation.cr.so'),
  (0x04ed7000, 0x04ef5ab4, 0x0001eab4, 'libcc_base.cr.so'),
  (0x04ef6000, 0x04fd9364, 0x000e3364, 'libcc_blink.cr.so'),
  (0x04fda000, 0x04fe2758, 0x00008758, 'libcc_debug.cr.so'),
  (0x04fe3000, 0x0500ae0c, 0x00027e0c, 'libcc_ipc.cr.so'),
  (0x0500b000, 0x05078f38, 0x0006df38, 'libcc_paint.cr.so'),
  (0x05079000, 0x0507e734, 0x00005734, 'libcdm_manager.cr.so'),
  (0x0507f000, 0x06f4d744, 0x01ece744, 'libchrome.cr.so'),
  (0x06f54000, 0x06feb830, 0x00097830, 'libchromium_sqlite3.cr.so'),
  (0x06fec000, 0x0706f554, 0x00083554, 'libclient.cr.so'),
  (0x07070000, 0x0708da60, 0x0001da60, 'libcloud_policy_proto_generated_compile.cr.so'),
  (0x0708e000, 0x07121f28, 0x00093f28, 'libcodec.cr.so'),
  (0x07122000, 0x07134ab8, 0x00012ab8, 'libcolor_space.cr.so'),
  (0x07135000, 0x07138614, 0x00003614, 'libcommon.cr.so'),
  (0x07139000, 0x0717c938, 0x00043938, 'libcompositor.cr.so'),
  (0x0717d000, 0x0923d78c, 0x020c078c, 'libcontent.cr.so'),
  (0x0923e000, 0x092ae87c, 0x0007087c, 'libcontent_common_mojo_bindings_shared.cr.so'),
  (0x092af000, 0x092be718, 0x0000f718, 'libcontent_public_common_mojo_bindings_shared.cr.so'),
  (0x092bf000, 0x092d9a20, 0x0001aa20, 'libcrash_key.cr.so'),
  (0x092da000, 0x092eda58, 0x00013a58, 'libcrcrypto.cr.so'),
  (0x092ee000, 0x092f16e0, 0x000036e0, 'libdevice_base.cr.so'),
  (0x092f2000, 0x092fe8d8, 0x0000c8d8, 'libdevice_event_log.cr.so'),
  (0x092ff000, 0x093026a4, 0x000036a4, 'libdevice_features.cr.so'),
  (0x09303000, 0x093f1220, 0x000ee220, 'libdevice_gamepad.cr.so'),
  (0x093f2000, 0x09437f54, 0x00045f54, 'libdevice_vr_mojo_bindings.cr.so'),
  (0x09438000, 0x0954c168, 0x00114168, 'libdevice_vr_mojo_bindings_blink.cr.so'),
  (0x0954d000, 0x0955d720, 0x00010720, 'libdevice_vr_mojo_bindings_shared.cr.so'),
  (0x0955e000, 0x0956b9c0, 0x0000d9c0, 'libdevices.cr.so'),
  (0x0956c000, 0x0957cae8, 0x00010ae8, 'libdiscardable_memory_client.cr.so'),
  (0x0957d000, 0x09588854, 0x0000b854, 'libdiscardable_memory_common.cr.so'),
  (0x09589000, 0x0959cbb4, 0x00013bb4, 'libdiscardable_memory_service.cr.so'),
  (0x0959d000, 0x095b6b90, 0x00019b90, 'libdisplay.cr.so'),
  (0x095b7000, 0x095be930, 0x00007930, 'libdisplay_types.cr.so'),
  (0x095bf000, 0x095c46c4, 0x000056c4, 'libdisplay_util.cr.so'),
  (0x095c5000, 0x095f54a4, 0x000304a4, 'libdomain_reliability.cr.so'),
  (0x095f6000, 0x0966fe08, 0x00079e08, 'libembedder.cr.so'),
  (0x09670000, 0x096735f8, 0x000035f8, 'libembedder_switches.cr.so'),
  (0x09674000, 0x096a3460, 0x0002f460, 'libevents.cr.so'),
  (0x096a4000, 0x096b6d40, 0x00012d40, 'libevents_base.cr.so'),
  (0x096b7000, 0x0981a778, 0x00163778, 'libffmpeg.cr.so'),
  (0x0981b000, 0x09945c94, 0x0012ac94, 'libfido.cr.so'),
  (0x09946000, 0x09a330dc, 0x000ed0dc, 'libfingerprint.cr.so'),
  (0x09a34000, 0x09b53170, 0x0011f170, 'libfreetype_harfbuzz.cr.so'),
  (0x09b54000, 0x09bc5c5c, 0x00071c5c, 'libgcm.cr.so'),
  (0x09bc6000, 0x09cc8584, 0x00102584, 'libgeolocation.cr.so'),
  (0x09cc9000, 0x09cdc8d4, 0x000138d4, 'libgeometry.cr.so'),
  (0x09cdd000, 0x09cec8b4, 0x0000f8b4, 'libgeometry_skia.cr.so'),
  (0x09ced000, 0x09d10e14, 0x00023e14, 'libgesture_detection.cr.so'),
  (0x09d11000, 0x09d7595c, 0x0006495c, 'libgfx.cr.so'),
  (0x09d76000, 0x09d7d7cc, 0x000077cc, 'libgfx_ipc.cr.so'),
  (0x09d7e000, 0x09d82708, 0x00004708, 'libgfx_ipc_buffer_types.cr.so'),
  (0x09d83000, 0x09d89748, 0x00006748, 'libgfx_ipc_color.cr.so'),
  (0x09d8a000, 0x09d8f6f4, 0x000056f4, 'libgfx_ipc_geometry.cr.so'),
  (0x09d90000, 0x09d94754, 0x00004754, 'libgfx_ipc_skia.cr.so'),
  (0x09d95000, 0x09d9869c, 0x0000369c, 'libgfx_switches.cr.so'),
  (0x09d99000, 0x09dba0ac, 0x000210ac, 'libgin.cr.so'),
  (0x09dbb000, 0x09e0a8cc, 0x0004f8cc, 'libgl_in_process_context.cr.so'),
  (0x09e0b000, 0x09e17a18, 0x0000ca18, 'libgl_init.cr.so'),
  (0x09e18000, 0x09ee34e4, 0x000cb4e4, 'libgl_wrapper.cr.so'),
  (0x09ee4000, 0x0a1a2e00, 0x002bee00, 'libgles2.cr.so'),
  (0x0a1a3000, 0x0a24556c, 0x000a256c, 'libgles2_implementation.cr.so'),
  (0x0a246000, 0x0a267038, 0x00021038, 'libgles2_utils.cr.so'),
  (0x0a268000, 0x0a3288e4, 0x000c08e4, 'libgpu.cr.so'),
  (0x0a329000, 0x0a3627ec, 0x000397ec, 'libgpu_ipc_service.cr.so'),
  (0x0a363000, 0x0a388a18, 0x00025a18, 'libgpu_util.cr.so'),
  (0x0a389000, 0x0a506d8c, 0x0017dd8c, 'libhost.cr.so'),
  (0x0a507000, 0x0a6f0ec0, 0x001e9ec0, 'libicui18n.cr.so'),
  (0x0a6f1000, 0x0a83b4c8, 0x0014a4c8, 'libicuuc.cr.so'),
  (0x0a83c000, 0x0a8416e4, 0x000056e4, 'libinterfaces_shared.cr.so'),
  (0x0a842000, 0x0a87e2a0, 0x0003c2a0, 'libipc.cr.so'),
  (0x0a87f000, 0x0a88c98c, 0x0000d98c, 'libipc_mojom.cr.so'),
  (0x0a88d000, 0x0a8926e4, 0x000056e4, 'libipc_mojom_shared.cr.so'),
  (0x0a893000, 0x0a8a1e18, 0x0000ee18, 'libkeyed_service_content.cr.so'),
  (0x0a8a2000, 0x0a8b4a30, 0x00012a30, 'libkeyed_service_core.cr.so'),
  (0x0a8b5000, 0x0a930a80, 0x0007ba80, 'libleveldatabase.cr.so'),
  (0x0a931000, 0x0a9b3908, 0x00082908, 'libmanager.cr.so'),
  (0x0a9b4000, 0x0aea9bb4, 0x004f5bb4, 'libmedia.cr.so'),
  (0x0aeaa000, 0x0b08cb88, 0x001e2b88, 'libmedia_blink.cr.so'),
  (0x0b08d000, 0x0b0a4728, 0x00017728, 'libmedia_devices_mojo_bindings_shared.cr.so'),
  (0x0b0a5000, 0x0b1943ec, 0x000ef3ec, 'libmedia_gpu.cr.so'),
  (0x0b195000, 0x0b2d07d4, 0x0013b7d4, 'libmedia_mojo_services.cr.so'),
  (0x0b2d1000, 0x0b2d4760, 0x00003760, 'libmessage_center.cr.so'),
  (0x0b2d5000, 0x0b2e0938, 0x0000b938, 'libmessage_support.cr.so'),
  (0x0b2e1000, 0x0b2f3ad0, 0x00012ad0, 'libmetrics_cpp.cr.so'),
  (0x0b2f4000, 0x0b313bb8, 0x0001fbb8, 'libmidi.cr.so'),
  (0x0b314000, 0x0b31b848, 0x00007848, 'libmojo_base_lib.cr.so'),
  (0x0b31c000, 0x0b3329f8, 0x000169f8, 'libmojo_base_mojom.cr.so'),
  (0x0b333000, 0x0b34b98c, 0x0001898c, 'libmojo_base_mojom_blink.cr.so'),
  (0x0b34c000, 0x0b354700, 0x00008700, 'libmojo_base_mojom_shared.cr.so'),
  (0x0b355000, 0x0b3608b0, 0x0000b8b0, 'libmojo_base_shared_typemap_traits.cr.so'),
  (0x0b361000, 0x0b3ad454, 0x0004c454, 'libmojo_edk.cr.so'),
  (0x0b3ae000, 0x0b3c4a20, 0x00016a20, 'libmojo_edk_ports.cr.so'),
  (0x0b3c5000, 0x0b3d38a0, 0x0000e8a0, 'libmojo_mojom_bindings.cr.so'),
  (0x0b3d4000, 0x0b3da6e8, 0x000066e8, 'libmojo_mojom_bindings_shared.cr.so'),
  (0x0b3db000, 0x0b3e27f0, 0x000077f0, 'libmojo_public_system.cr.so'),
  (0x0b3e3000, 0x0b3fa9fc, 0x000179fc, 'libmojo_public_system_cpp.cr.so'),
  (0x0b3fb000, 0x0b407728, 0x0000c728, 'libmojom_core_shared.cr.so'),
  (0x0b408000, 0x0b421744, 0x00019744, 'libmojom_platform_shared.cr.so'),
  (0x0b422000, 0x0b43451c, 0x0001251c, 'libnative_theme.cr.so'),
  (0x0b435000, 0x0baaa1bc, 0x006751bc, 'libnet.cr.so'),
  (0x0bac4000, 0x0bb74670, 0x000b0670, 'libnetwork_cpp.cr.so'),
  (0x0bb75000, 0x0bbaee8c, 0x00039e8c, 'libnetwork_cpp_base.cr.so'),
  (0x0bbaf000, 0x0bd21844, 0x00172844, 'libnetwork_service.cr.so'),
  (0x0bd22000, 0x0bd256e4, 0x000036e4, 'libnetwork_session_configurator.cr.so'),
  (0x0bd26000, 0x0bd33734, 0x0000d734, 'libonc.cr.so'),
  (0x0bd34000, 0x0bd9ce18, 0x00068e18, 'libperfetto.cr.so'),
  (0x0bd9d000, 0x0bda4854, 0x00007854, 'libplatform.cr.so'),
  (0x0bda5000, 0x0bec5ce4, 0x00120ce4, 'libpolicy_component.cr.so'),
  (0x0bec6000, 0x0bf5ab58, 0x00094b58, 'libpolicy_proto.cr.so'),
  (0x0bf5b000, 0x0bf86fbc, 0x0002bfbc, 'libprefs.cr.so'),
  (0x0bf87000, 0x0bfa5d74, 0x0001ed74, 'libprinting.cr.so'),
  (0x0bfa6000, 0x0bfe0e80, 0x0003ae80, 'libprotobuf_lite.cr.so'),
  (0x0bfe1000, 0x0bff0a18, 0x0000fa18, 'libproxy_config.cr.so'),
  (0x0bff1000, 0x0c0f6654, 0x00105654, 'libpublic.cr.so'),
  (0x0c0f7000, 0x0c0fa6a4, 0x000036a4, 'librange.cr.so'),
  (0x0c0fb000, 0x0c118058, 0x0001d058, 'libraster.cr.so'),
  (0x0c119000, 0x0c133d00, 0x0001ad00, 'libresource_coordinator_cpp.cr.so'),
  (0x0c134000, 0x0c1396a0, 0x000056a0, 'libresource_coordinator_cpp_base.cr.so'),
  (0x0c13a000, 0x0c1973b8, 0x0005d3b8, 'libresource_coordinator_public_mojom.cr.so'),
  (0x0c198000, 0x0c2033e8, 0x0006b3e8, 'libresource_coordinator_public_mojom_blink.cr.so'),
  (0x0c204000, 0x0c219744, 0x00015744, 'libresource_coordinator_public_mojom_shared.cr.so'),
  (0x0c21a000, 0x0c21e700, 0x00004700, 'libsandbox.cr.so'),
  (0x0c21f000, 0x0c22f96c, 0x0001096c, 'libsandbox_services.cr.so'),
  (0x0c230000, 0x0c249d58, 0x00019d58, 'libseccomp_bpf.cr.so'),
  (0x0c24a000, 0x0c24e714, 0x00004714, 'libseccomp_starter_android.cr.so'),
  (0x0c24f000, 0x0c4ae9f0, 0x0025f9f0, 'libservice.cr.so'),
  (0x0c4af000, 0x0c4c3ae4, 0x00014ae4, 'libservice_manager_cpp.cr.so'),
  (0x0c4c4000, 0x0c4cb708, 0x00007708, 'libservice_manager_cpp_types.cr.so'),
  (0x0c4cc000, 0x0c4fbe30, 0x0002fe30, 'libservice_manager_mojom.cr.so'),
  (0x0c4fc000, 0x0c532e78, 0x00036e78, 'libservice_manager_mojom_blink.cr.so'),
  (0x0c533000, 0x0c53669c, 0x0000369c, 'libservice_manager_mojom_constants.cr.so'),
  (0x0c537000, 0x0c53e85c, 0x0000785c, 'libservice_manager_mojom_constants_blink.cr.so'),
  (0x0c53f000, 0x0c542668, 0x00003668, 'libservice_manager_mojom_constants_shared.cr.so'),
  (0x0c543000, 0x0c54d700, 0x0000a700, 'libservice_manager_mojom_shared.cr.so'),
  (0x0c54e000, 0x0c8fc6ec, 0x003ae6ec, 'libsessions.cr.so'),
  (0x0c8fd000, 0x0c90a924, 0x0000d924, 'libshared_memory_support.cr.so'),
  (0x0c90b000, 0x0c9148ec, 0x000098ec, 'libshell_dialogs.cr.so'),
  (0x0c915000, 0x0cf8de70, 0x00678e70, 'libskia.cr.so'),
  (0x0cf8e000, 0x0cf978bc, 0x000098bc, 'libsnapshot.cr.so'),
  (0x0cf98000, 0x0cfb7d9c, 0x0001fd9c, 'libsql.cr.so'),
  (0x0cfb8000, 0x0cfbe744, 0x00006744, 'libstartup_tracing.cr.so'),
  (0x0cfbf000, 0x0d19b4e4, 0x001dc4e4, 'libstorage_browser.cr.so'),
  (0x0d19c000, 0x0d2a773c, 0x0010b73c, 'libstorage_common.cr.so'),
  (0x0d2a8000, 0x0d2ac6fc, 0x000046fc, 'libsurface.cr.so'),
  (0x0d2ad000, 0x0d2baa98, 0x0000da98, 'libtracing.cr.so'),
  (0x0d2bb000, 0x0d2f36b0, 0x000386b0, 'libtracing_cpp.cr.so'),
  (0x0d2f4000, 0x0d326e70, 0x00032e70, 'libtracing_mojom.cr.so'),
  (0x0d327000, 0x0d33270c, 0x0000b70c, 'libtracing_mojom_shared.cr.so'),
  (0x0d333000, 0x0d46d804, 0x0013a804, 'libui_android.cr.so'),
  (0x0d46e000, 0x0d4cb3f8, 0x0005d3f8, 'libui_base.cr.so'),
  (0x0d4cc000, 0x0d4dbc40, 0x0000fc40, 'libui_base_ime.cr.so'),
  (0x0d4dc000, 0x0d4e58d4, 0x000098d4, 'libui_data_pack.cr.so'),
  (0x0d4e6000, 0x0d51d1e0, 0x000371e0, 'libui_devtools.cr.so'),
  (0x0d51e000, 0x0d52b984, 0x0000d984, 'libui_message_center_cpp.cr.so'),
  (0x0d52c000, 0x0d539a48, 0x0000da48, 'libui_touch_selection.cr.so'),
  (0x0d53a000, 0x0d55bc60, 0x00021c60, 'liburl.cr.so'),
  (0x0d55c000, 0x0d55f6b4, 0x000036b4, 'liburl_ipc.cr.so'),
  (0x0d560000, 0x0d5af110, 0x0004f110, 'liburl_matcher.cr.so'),
  (0x0d5b0000, 0x0d5e2fac, 0x00032fac, 'libuser_manager.cr.so'),
  (0x0d5e3000, 0x0d5e66e4, 0x000036e4, 'libuser_prefs.cr.so'),
  (0x0d5e7000, 0x0e3e1cc8, 0x00dfacc8, 'libv8.cr.so'),
  (0x0e3e2000, 0x0e400ae0, 0x0001eae0, 'libv8_libbase.cr.so'),
  (0x0e401000, 0x0e4d91d4, 0x000d81d4, 'libviz_common.cr.so'),
  (0x0e4da000, 0x0e4df7e4, 0x000057e4, 'libviz_resource_format.cr.so'),
  (0x0e4e0000, 0x0e5b7120, 0x000d7120, 'libweb_dialogs.cr.so'),
  (0x0e5b8000, 0x0e5c7a18, 0x0000fa18, 'libwebdata_common.cr.so'),
  (0x0e5c8000, 0x0e61bfe4, 0x00053fe4, 'libwtf.cr.so'),
]


# A small memory map fragment extracted from a tombstone for a process that
# had loaded the APK corresponding to _TEST_APK_LIBS above.
_TEST_MEMORY_MAP = r'''memory map:
12c00000-12ccafff rw-         0     cb000  /dev/ashmem/dalvik-main space (deleted)
12ccb000-130cafff rw-     cb000    400000  /dev/ashmem/dalvik-main space (deleted)
130cb000-32bfffff ---    4cb000  1fb35000  /dev/ashmem/dalvik-main space (deleted)
32c00000-32c00fff rw-         0      1000  /dev/ashmem/dalvik-main space 1 (deleted)
32c01000-52bfffff ---      1000  1ffff000  /dev/ashmem/dalvik-main space 1 (deleted)
6f3b8000-6fd90fff rw-         0    9d9000  /data/dalvik-cache/x86/system@framework@boot.art
6fd91000-71c42fff r--         0   1eb2000  /data/dalvik-cache/x86/system@framework@boot.oat
71c43000-7393efff r-x   1eb2000   1cfc000  /data/dalvik-cache/x86/system@framework@boot.oat (load base 0x71c43000)
7393f000-7393ffff rw-   3bae000      1000  /data/dalvik-cache/x86/system@framework@boot.oat
73940000-73a1bfff rw-         0     dc000  /dev/ashmem/dalvik-zygote space (deleted)
73a1c000-73a1cfff rw-         0      1000  /dev/ashmem/dalvik-non moving space (deleted)
73a1d000-73a2dfff rw-      1000     11000  /dev/ashmem/dalvik-non moving space (deleted)
73a2e000-77540fff ---     12000   3b13000  /dev/ashmem/dalvik-non moving space (deleted)
77541000-7793ffff rw-   3b25000    3ff000  /dev/ashmem/dalvik-non moving space (deleted)
923aa000-92538fff r--    8a9000    18f000  /data/app/com.example.app-2/base.apk
92539000-9255bfff r--         0     23000  /data/data/com.example.app/app_data/paks/es.pak@162db1c6689
9255c000-92593fff r--    213000     38000  /data/app/com.example.app-2/base.apk
92594000-925c0fff r--    87d000     2d000  /data/app/com.example.app-2/base.apk
925c1000-927d3fff r--    a37000    213000  /data/app/com.example.app-2/base.apk
927d4000-92e07fff r--    24a000    634000  /data/app/com.example.app-2/base.apk
92e08000-92e37fff r--   a931000     30000  /data/app/com.example.app-2/base.apk
92e38000-92e86fff r-x   a961000     4f000  /data/app/com.example.app-2/base.apk
92e87000-92e8afff rw-   a9b0000      4000  /data/app/com.example.app-2/base.apk
92e8b000-92e8bfff rw-         0      1000
92e8c000-92e9dfff r--   d5b0000     12000  /data/app/com.example.app-2/base.apk
92e9e000-92ebcfff r-x   d5c2000     1f000  /data/app/com.example.app-2/base.apk
92ebd000-92ebefff rw-   d5e1000      2000  /data/app/com.example.app-2/base.apk
92ebf000-92ebffff rw-         0      1000
'''

# list of (address, size, path, offset)  tuples that must appear in
# _TEST_MEMORY_MAP. Not all sections need to be listed.
_TEST_MEMORY_MAP_SECTIONS = [
  (0x923aa000, 0x18f000, '/data/app/com.example.app-2/base.apk', 0x8a9000),
  (0x9255c000, 0x038000, '/data/app/com.example.app-2/base.apk', 0x213000),
  (0x92594000, 0x02d000, '/data/app/com.example.app-2/base.apk', 0x87d000),
  (0x925c1000, 0x213000, '/data/app/com.example.app-2/base.apk', 0xa37000),
]

_EXPECTED_TEST_MEMORY_MAP = r'''memory map:
12c00000-12ccafff rw-         0     cb000  /dev/ashmem/dalvik-main space (deleted)
12ccb000-130cafff rw-     cb000    400000  /dev/ashmem/dalvik-main space (deleted)
130cb000-32bfffff ---    4cb000  1fb35000  /dev/ashmem/dalvik-main space (deleted)
32c00000-32c00fff rw-         0      1000  /dev/ashmem/dalvik-main space 1 (deleted)
32c01000-52bfffff ---      1000  1ffff000  /dev/ashmem/dalvik-main space 1 (deleted)
6f3b8000-6fd90fff rw-         0    9d9000  /data/dalvik-cache/x86/system@framework@boot.art
6fd91000-71c42fff r--         0   1eb2000  /data/dalvik-cache/x86/system@framework@boot.oat
71c43000-7393efff r-x   1eb2000   1cfc000  /data/dalvik-cache/x86/system@framework@boot.oat (load base 0x71c43000)
7393f000-7393ffff rw-   3bae000      1000  /data/dalvik-cache/x86/system@framework@boot.oat
73940000-73a1bfff rw-         0     dc000  /dev/ashmem/dalvik-zygote space (deleted)
73a1c000-73a1cfff rw-         0      1000  /dev/ashmem/dalvik-non moving space (deleted)
73a1d000-73a2dfff rw-      1000     11000  /dev/ashmem/dalvik-non moving space (deleted)
73a2e000-77540fff ---     12000   3b13000  /dev/ashmem/dalvik-non moving space (deleted)
77541000-7793ffff rw-   3b25000    3ff000  /dev/ashmem/dalvik-non moving space (deleted)
923aa000-92538fff r--    8a9000    18f000  /data/app/com.example.app-2/base.apk
92539000-9255bfff r--         0     23000  /data/data/com.example.app/app_data/paks/es.pak@162db1c6689
9255c000-92593fff r--    213000     38000  /data/app/com.example.app-2/base.apk
92594000-925c0fff r--    87d000     2d000  /data/app/com.example.app-2/base.apk
925c1000-927d3fff r--    a37000    213000  /data/app/com.example.app-2/base.apk
927d4000-92e07fff r--    24a000    634000  /data/app/com.example.app-2/base.apk
92e08000-92e37fff r--   a931000     30000  /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x0)
92e38000-92e86fff r-x   a961000     4f000  /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x30000)
92e87000-92e8afff rw-   a9b0000      4000  /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x7f000)
92e8b000-92e8bfff rw-         0      1000
92e8c000-92e9dfff r--   d5b0000     12000  /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x0)
92e9e000-92ebcfff r-x   d5c2000     1f000  /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x12000)
92ebd000-92ebefff rw-   d5e1000      2000  /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x31000)
92ebf000-92ebffff rw-         0      1000
'''

# Example stack section, taken from the same tombstone that _TEST_MEMORY_MAP
# was extracted from.
_TEST_STACK = r'''stack:
        bf89a070  b7439468  /system/lib/libc.so
        bf89a074  bf89a1e4  [stack]
        bf89a078  932d4000  /data/app/com.example.app-2/base.apk
        bf89a07c  b73bfbc9  /system/lib/libc.so (pthread_mutex_lock+65)
        bf89a080  00000000
        bf89a084  4000671c  /dev/ashmem/dalvik-main space 1 (deleted)
        bf89a088  932d1d86  /data/app/com.example.app-2/base.apk
        bf89a08c  b743671c  /system/lib/libc.so
        bf89a090  b77f8c00  /system/bin/linker
        bf89a094  b743cc90
        bf89a098  932d1d4a  /data/app/com.example.app-2/base.apk
        bf89a09c  b73bf271  /system/lib/libc.so (__pthread_internal_find(long)+65)
        bf89a0a0  b743cc90
        bf89a0a4  bf89a0b0  [stack]
        bf89a0a8  bf89a0b8  [stack]
        bf89a0ac  00000008
        ........  ........
  #00  bf89a0b0  00000006
        bf89a0b4  00000002
        bf89a0b8  b743671c  /system/lib/libc.so
        bf89a0bc  b73bf5d9  /system/lib/libc.so (pthread_kill+71)
  #01  bf89a0c0  00006937
        bf89a0c4  00006937
        bf89a0c8  00000006
        bf89a0cc  b77fd3a9  /system/bin/app_process32 (sigprocmask+141)
        bf89a0d0  00000002
        bf89a0d4  bf89a0ec  [stack]
        bf89a0d8  00000000
        bf89a0dc  b743671c  /system/lib/libc.so
        bf89a0e0  bf89a12c  [stack]
        bf89a0e4  bf89a1e4  [stack]
        bf89a0e8  932d1d4a  /data/app/com.example.app-2/base.apk
        bf89a0ec  b7365206  /system/lib/libc.so (raise+37)
  #02  bf89a0f0  b77f8c00  /system/bin/linker
        bf89a0f4  00000006
        bf89a0f8  b7439468  /system/lib/libc.so
        bf89a0fc  b743671c  /system/lib/libc.so
        bf89a100  bf89a12c  [stack]
        bf89a104  b743671c  /system/lib/libc.so
        bf89a108  bf89a12c  [stack]
        bf89a10c  b735e9e5  /system/lib/libc.so (abort+81)
  #03  bf89a110  00000006
        bf89a114  bf89a12c  [stack]
        bf89a118  00000000
        bf89a11c  b55a3d3b  /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::DefaultLogHandler(google::protobuf::LogLevel, char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)+99)
        bf89a120  b7439468  /system/lib/libc.so
        bf89a124  b55ba38d  /system/lib/libprotobuf-cpp-lite.so
        bf89a128  b55ba408  /system/lib/libprotobuf-cpp-lite.so
        bf89a12c  ffffffdf
        bf89a130  0000003d
        bf89a134  adfedf00  [anon:libc_malloc]
        bf89a138  bf89a158  [stack]
  #04  bf89a13c  a0cee7f0  /data/app/com.example.app-2/base.apk
        bf89a140  b55c1cb0  /system/lib/libprotobuf-cpp-lite.so
        bf89a144  bf89a1e4  [stack]
'''

# Expected value of _TEST_STACK after translation of addresses in the APK
# into offsets into libraries.
_EXPECTED_STACK = r'''stack:
        bf89a070  b7439468  /system/lib/libc.so
        bf89a074  bf89a1e4  [stack]
        bf89a078  932d4000  /data/app/com.example.app-2/base.apk
        bf89a07c  b73bfbc9  /system/lib/libc.so (pthread_mutex_lock+65)
        bf89a080  00000000
        bf89a084  4000671c  /dev/ashmem/dalvik-main space 1 (deleted)
        bf89a088  932d1d86  /data/app/com.example.app-2/base.apk
        bf89a08c  b743671c  /system/lib/libc.so
        bf89a090  b77f8c00  /system/bin/linker
        bf89a094  b743cc90
        bf89a098  932d1d4a  /data/app/com.example.app-2/base.apk
        bf89a09c  b73bf271  /system/lib/libc.so (__pthread_internal_find(long)+65)
        bf89a0a0  b743cc90
        bf89a0a4  bf89a0b0  [stack]
        bf89a0a8  bf89a0b8  [stack]
        bf89a0ac  00000008
        ........  ........
  #00  bf89a0b0  00000006
        bf89a0b4  00000002
        bf89a0b8  b743671c  /system/lib/libc.so
        bf89a0bc  b73bf5d9  /system/lib/libc.so (pthread_kill+71)
  #01  bf89a0c0  00006937
        bf89a0c4  00006937
        bf89a0c8  00000006
        bf89a0cc  b77fd3a9  /system/bin/app_process32 (sigprocmask+141)
        bf89a0d0  00000002
        bf89a0d4  bf89a0ec  [stack]
        bf89a0d8  00000000
        bf89a0dc  b743671c  /system/lib/libc.so
        bf89a0e0  bf89a12c  [stack]
        bf89a0e4  bf89a1e4  [stack]
        bf89a0e8  932d1d4a  /data/app/com.example.app-2/base.apk
        bf89a0ec  b7365206  /system/lib/libc.so (raise+37)
  #02  bf89a0f0  b77f8c00  /system/bin/linker
        bf89a0f4  00000006
        bf89a0f8  b7439468  /system/lib/libc.so
        bf89a0fc  b743671c  /system/lib/libc.so
        bf89a100  bf89a12c  [stack]
        bf89a104  b743671c  /system/lib/libc.so
        bf89a108  bf89a12c  [stack]
        bf89a10c  b735e9e5  /system/lib/libc.so (abort+81)
  #03  bf89a110  00000006
        bf89a114  bf89a12c  [stack]
        bf89a118  00000000
        bf89a11c  b55a3d3b  /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::DefaultLogHandler(google::protobuf::LogLevel, char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)+99)
        bf89a120  b7439468  /system/lib/libc.so
        bf89a124  b55ba38d  /system/lib/libprotobuf-cpp-lite.so
        bf89a128  b55ba408  /system/lib/libprotobuf-cpp-lite.so
        bf89a12c  ffffffdf
        bf89a130  0000003d
        bf89a134  adfedf00  [anon:libc_malloc]
        bf89a138  bf89a158  [stack]
  #04  bf89a13c  a0cee7f0  /data/app/com.example.app-2/base.apk
        bf89a140  b55c1cb0  /system/lib/libprotobuf-cpp-lite.so
        bf89a144  bf89a1e4  [stack]
'''

_TEST_BACKTRACE = r'''backtrace:
    #00 pc 00084126  /system/lib/libc.so (tgkill+22)
    #01 pc 000815d8  /system/lib/libc.so (pthread_kill+70)
    #02 pc 00027205  /system/lib/libc.so (raise+36)
    #03 pc 000209e4  /system/lib/libc.so (abort+80)
    #04 pc 0000cf73  /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogMessage::Finish()+117)
    #05 pc 0000cf8e  /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogFinisher::operator=(google::protobuf::internal::LogMessage&)+26)
    #06 pc 0000d27f  /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::VerifyVersion(int, int, char const*)+574)
    #07 pc 007cd236  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #08 pc 000111a9  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000)
    #09 pc 00013228  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000)
    #10 pc 000131de  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000)
    #11 pc 007cd2d8  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #12 pc 007cd956  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #13 pc 007c2d4a  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #14 pc 009fc9f1  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #15 pc 009fc8ea  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #16 pc 00561c63  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #17 pc 0106fbdb  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #18 pc 004d7371  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #19 pc 004d8159  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #20 pc 004d7b96  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #21 pc 004da4b6  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #22 pc 005ab66c  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
    #23 pc 005afca2  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
    #24 pc 0000cae8  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000)
    #25 pc 00ce864f  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
    #26 pc 00ce8dfa  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
    #27 pc 00ce74c6  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
    #28 pc 00004616  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x961e000)
    #29 pc 00ce8215  /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000)
    #30 pc 0013d8c7  /system/lib/libart.so (art_quick_generic_jni_trampoline+71)
    #31 pc 00137c52  /system/lib/libart.so (art_quick_invoke_static_stub+418)
    #32 pc 00143651  /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+353)
    #33 pc 005e06ae  /system/lib/libart.so (artInterpreterToCompiledCodeBridge+190)
    #34 pc 00328b5d  /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
    #35 pc 0032cfc0  /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160)
    #36 pc 000fc703  /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891)
    #37 pc 00300af7  /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
    #38 pc 00328b5d  /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
    #39 pc 0032cfc0  /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160)
    #40 pc 000fc703  /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891)
    #41 pc 00300af7  /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
    #42 pc 00328b5d  /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
    #43 pc 0032ebf9  /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)2, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+297)
    #44 pc 000fc955  /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+30485)
    #45 pc 00300af7  /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
    #46 pc 00328b5d  /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
    #47 pc 0033090c  /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)4, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+636)
    #48 pc 000fc67f  /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29759)
    #49 pc 00300700  /system/lib/libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame*)+128)
    #50 pc 00667c73  /system/lib/libart.so (artQuickToInterpreterBridge+808)
    #51 pc 0013d98d  /system/lib/libart.so (art_quick_to_interpreter_bridge+77)
    #52 pc 7264bc5b  /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000)
'''

_EXPECTED_BACKTRACE = r'''backtrace:
    #00 pc 00084126  /system/lib/libc.so (tgkill+22)
    #01 pc 000815d8  /system/lib/libc.so (pthread_kill+70)
    #02 pc 00027205  /system/lib/libc.so (raise+36)
    #03 pc 000209e4  /system/lib/libc.so (abort+80)
    #04 pc 0000cf73  /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogMessage::Finish()+117)
    #05 pc 0000cf8e  /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogFinisher::operator=(google::protobuf::internal::LogMessage&)+26)
    #06 pc 0000d27f  /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::VerifyVersion(int, int, char const*)+574)
    #07 pc 007cd236  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #08 pc 000111a9  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000)
    #09 pc 00013228  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000)
    #10 pc 000131de  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000)
    #11 pc 007cd2d8  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #12 pc 007cd956  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #13 pc 007c2d4a  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #14 pc 009fc9f1  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #15 pc 009fc8ea  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #16 pc 00561c63  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #17 pc 0106fbdb  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #18 pc 004d7371  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #19 pc 004d8159  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #20 pc 004d7b96  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #21 pc 004da4b6  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #22 pc 005ab66c  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
    #23 pc 005afca2  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
    #24 pc 0000cae8  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000)
    #25 pc 00ce864f  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
    #26 pc 00ce8dfa  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
    #27 pc 00ce74c6  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
    #28 pc 00004616  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libembedder.cr.so (offset 0x28000)
    #29 pc 00ce8215  /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000)
    #30 pc 0013d8c7  /system/lib/libart.so (art_quick_generic_jni_trampoline+71)
    #31 pc 00137c52  /system/lib/libart.so (art_quick_invoke_static_stub+418)
    #32 pc 00143651  /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+353)
    #33 pc 005e06ae  /system/lib/libart.so (artInterpreterToCompiledCodeBridge+190)
    #34 pc 00328b5d  /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
    #35 pc 0032cfc0  /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160)
    #36 pc 000fc703  /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891)
    #37 pc 00300af7  /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
    #38 pc 00328b5d  /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
    #39 pc 0032cfc0  /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160)
    #40 pc 000fc703  /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891)
    #41 pc 00300af7  /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
    #42 pc 00328b5d  /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
    #43 pc 0032ebf9  /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)2, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+297)
    #44 pc 000fc955  /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+30485)
    #45 pc 00300af7  /system/lib/libart.so (artInterpreterToInterpreterBridge+188)
    #46 pc 00328b5d  /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445)
    #47 pc 0033090c  /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)4, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+636)
    #48 pc 000fc67f  /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29759)
    #49 pc 00300700  /system/lib/libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame*)+128)
    #50 pc 00667c73  /system/lib/libart.so (artQuickToInterpreterBridge+808)
    #51 pc 0013d98d  /system/lib/libart.so (art_quick_to_interpreter_bridge+77)
    #52 pc 7264bc5b  /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000)
'''

_EXPECTED_BACKTRACE_OFFSETS_MAP = {
  '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so':
      set([
          0x1c000 + 0x111a9,
          0x1c000 + 0x13228,
          0x1c000 + 0x131de,
      ]),

  '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so':
      set([
          0x90e000 + 0x7cd236,
          0x90e000 + 0x7cd2d8,
          0x90e000 + 0x7cd956,
          0x90e000 + 0x7c2d4a,
          0x90e000 + 0x9fc9f1,
          0x90e000 + 0x9fc8ea,
          0x90e000 + 0x561c63,
          0x90e000 + 0x106fbdb,
          0x90e000 + 0x4d7371,
          0x90e000 + 0x4d8159,
          0x90e000 + 0x4d7b96,
          0x90e000 + 0x4da4b6,
          0x90e000 + 0xcae8,
      ]),
  '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so':
      set([
          0xc2d000 + 0x5ab66c,
          0xc2d000 + 0x5afca2,
          0xc2d000 + 0xce864f,
          0xc2d000 + 0xce8dfa,
          0xc2d000 + 0xce74c6,
          0xc2d000 + 0xce8215,
      ]),
  '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libembedder.cr.so':
      set([
          0x28000 + 0x4616,
      ])
}

# pylint: enable=line-too-long

_ONE_MB = 1024 * 1024
_TEST_SYMBOL_DATA = {
  # Regular symbols
  0: 'mock_sym_for_addr_0 [mock_src/libmock1.so.c:0]',
  0x1000: 'mock_sym_for_addr_4096 [mock_src/libmock1.so.c:4096]',

  # Symbols without source file path.
  _ONE_MB: 'mock_sym_for_addr_1048576 [??:0]',
  _ONE_MB + 0x8234: 'mock_sym_for_addr_1081908 [??:0]',

  # Unknown symbol.
  2 * _ONE_MB: '?? [??:0]',

  # Inlined symbol.
  3 * _ONE_MB:
    'mock_sym_for_addr_3145728_inner [mock_src/libmock1.so.c:3145728]',
}

@contextlib.contextmanager
def _TempDir():
  dirname = tempfile.mkdtemp()
  try:
    yield dirname
  finally:
    shutil.rmtree(dirname)


def _TouchFile(path):
  # Create parent directories.
  try:
    os.makedirs(os.path.dirname(path))
  except OSError:
    pass
  with open(path, 'a'):
    os.utime(path, None)

class MockApkTranslator(object):
  """A mock ApkLibraryPathTranslator object used for testing."""

  # Regex that matches the content of APK native library map files generated
  # with apk_lib_dump.py.
  _RE_MAP_FILE = re.compile(
      r'0x(?P<file_start>[0-9a-f]+)\s+' +
      r'0x(?P<file_end>[0-9a-f]+)\s+' +
      r'0x(?P<file_size>[0-9a-f]+)\s+' +
      r'0x(?P<lib_path>[0-9a-f]+)\s+')

  def __init__(self, test_apk_libs=None):
    """Initialize instance.

    Args:
      test_apk_libs: Optional list of (file_start, file_end, size, lib_path)
        tuples, like _TEST_APK_LIBS for example. This will be used to
        implement TranslatePath().
    """
    self._apk_libs = []
    if test_apk_libs:
      self._AddLibEntries(test_apk_libs)

  def _AddLibEntries(self, entries):
    self._apk_libs = sorted(self._apk_libs + entries,
                            lambda x, y: cmp(x[0], y[0]))

  def ReadMapFile(self, file_path):
    """Read an .apk.native-libs file that was produced with apk_lib_dump.py.

    Args:
      file_path: input path to .apk.native-libs file. Its format is
        essentially: 0x<start>  0x<end> 0x<size> <library-path>
    """
    new_libs = []
    with open(file_path) as f:
      for line in f.readlines():
        m = MockApkTranslator._RE_MAP_FILE.match(line)
        if m:
          file_start = int(m.group('file_start'), 16)
          file_end = int(m.group('file_end'), 16)
          file_size = int(m.group('file_size'), 16)
          lib_path = m.group('lib_path')
          # Sanity check
          if file_start + file_size != file_end:
            logging.warning('%s: Inconsistent (start, end, size) values '
                            '(0x%x, 0x%x, 0x%x)',
                            file_path, file_start, file_end, file_size)
          else:
            new_libs.append((file_start, file_end, file_size, lib_path))

    self._AddLibEntries(new_libs)

  def TranslatePath(self, lib_path, lib_offset):
    """Translate an APK file path + offset into a library path + offset."""
    min_pos = 0
    max_pos = len(self._apk_libs)
    while min_pos < max_pos:
      mid_pos = (min_pos + max_pos) / 2
      mid_entry = self._apk_libs[mid_pos]
      mid_offset = mid_entry[0]
      mid_size = mid_entry[2]
      if lib_offset < mid_offset:
        max_pos = mid_pos
      elif lib_offset >= mid_offset + mid_size:
        min_pos = mid_pos + 1
      else:
        # Found it
        new_path = '%s!lib/%s' % (lib_path, mid_entry[3])
        new_offset = lib_offset - mid_offset
        return (new_path, new_offset)

    return lib_path, lib_offset


class HostLibraryFinderTest(unittest.TestCase):

  def testEmpty(self):
    finder = symbol_utils.HostLibraryFinder()
    self.assertIsNone(finder.Find('/data/data/com.example.app-1/lib/libfoo.so'))
    self.assertIsNone(
        finder.Find('/data/data/com.example.app-1/base.apk!lib/libfoo.so'))


  def testSimpleDirectory(self):
    finder = symbol_utils.HostLibraryFinder()
    with _TempDir() as tmp_dir:
      host_libfoo_path = os.path.join(tmp_dir, 'libfoo.so')
      host_libbar_path = os.path.join(tmp_dir, 'libbar.so')
      _TouchFile(host_libfoo_path)
      _TouchFile(host_libbar_path)

      finder.AddSearchDir(tmp_dir)

      # Regular library path (extracted at installation by the PackageManager).
      # Note that the extraction path has changed between Android releases,
      # i.e. it can be /data/app/, /data/data/ or /data/app-lib/ depending
      # on the system.
      self.assertEqual(
          host_libfoo_path,
          finder.Find('/data/app-lib/com.example.app-1/lib/libfoo.so'))

      # Verify that the path doesn't really matter
      self.assertEqual(
          host_libfoo_path,
          finder.Find('/whatever/what.apk!lib/libfoo.so'))

      self.assertEqual(
          host_libbar_path,
          finder.Find('/data/data/com.example.app-1/lib/libbar.so'))

      self.assertIsNone(
          finder.Find('/data/data/com.example.app-1/lib/libunknown.so'))


  def testMultipleDirectories(self):
    with _TempDir() as tmp_dir:
      # Create the following files:
      #   <tmp_dir>/aaa/
      #      libfoo.so
      #   <tmp_dir>/bbb/
      #      libbar.so
      #      libfoo.so    (this one should never be seen because 'aaa'
      #                    shall be first in the search path list).
      #
      aaa_dir = os.path.join(tmp_dir, 'aaa')
      bbb_dir = os.path.join(tmp_dir, 'bbb')
      os.makedirs(aaa_dir)
      os.makedirs(bbb_dir)

      host_libfoo_path = os.path.join(aaa_dir, 'libfoo.so')
      host_libbar_path = os.path.join(bbb_dir, 'libbar.so')
      host_libfoo2_path = os.path.join(bbb_dir, 'libfoo.so')

      _TouchFile(host_libfoo_path)
      _TouchFile(host_libbar_path)
      _TouchFile(host_libfoo2_path)

      finder = symbol_utils.HostLibraryFinder()
      finder.AddSearchDir(aaa_dir)
      finder.AddSearchDir(bbb_dir)

      self.assertEqual(
          host_libfoo_path,
          finder.Find('/data/data/com.example.app-1/lib/libfoo.so'))

      self.assertEqual(
          host_libfoo_path,
          finder.Find('/data/whatever/base.apk!lib/libfoo.so'))

      self.assertEqual(
          host_libbar_path,
          finder.Find('/data/data/com.example.app-1/lib/libbar.so'))

      self.assertIsNone(
          finder.Find('/data/data/com.example.app-1/lib/libunknown.so'))


class ElfSymbolResolverTest(unittest.TestCase):

  def testCreation(self):
    resolver = symbol_utils.ElfSymbolResolver(
        addr2line_path_for_tests=_MOCK_A2L_PATH)
    self.assertTrue(resolver)

  def testWithSimpleOffsets(self):
    resolver = symbol_utils.ElfSymbolResolver(
        addr2line_path_for_tests=_MOCK_A2L_PATH)
    resolver.SetAndroidAbi('ignored-abi')

    for addr, expected_sym in _TEST_SYMBOL_DATA.iteritems():
      self.assertEqual(resolver.FindSymbolInfo('/some/path/libmock1.so', addr),
                       expected_sym)

  def testWithPreResolvedSymbols(self):
    resolver = symbol_utils.ElfSymbolResolver(
        addr2line_path_for_tests=_MOCK_A2L_PATH)
    resolver.SetAndroidAbi('ignored-abi')
    resolver.AddLibraryOffsets('/some/path/libmock1.so',
                               _TEST_SYMBOL_DATA.keys())

    resolver.DisallowSymbolizerForTesting()

    for addr, expected_sym in _TEST_SYMBOL_DATA.iteritems():
      sym_info = resolver.FindSymbolInfo('/some/path/libmock1.so', addr)
      self.assertIsNotNone(sym_info, 'None symbol info for addr %x' % addr)
      self.assertEqual(
          sym_info, expected_sym,
          'Invalid symbol info for addr %x [%s] expected [%s]' % (
              addr, sym_info, expected_sym))


class MemoryMapTest(unittest.TestCase):

  def testCreation(self):
    mem_map = symbol_utils.MemoryMap('test-abi32')
    self.assertIsNone(mem_map.FindSectionForAddress(0))

  def testParseLines(self):
    mem_map = symbol_utils.MemoryMap('test-abi32')
    mem_map.ParseLines(_TEST_MEMORY_MAP.splitlines())
    for exp_addr, exp_size, exp_path, exp_offset in _TEST_MEMORY_MAP_SECTIONS:
      text = '(addr:%x, size:%x, path:%s, offset=%x)' % (
          exp_addr, exp_size, exp_path, exp_offset)

      t = mem_map.FindSectionForAddress(exp_addr)
      self.assertTrue(t, 'Could not find %s' % text)
      self.assertEqual(t.address, exp_addr)
      self.assertEqual(t.size, exp_size)
      self.assertEqual(t.offset, exp_offset)
      self.assertEqual(t.path, exp_path)

  def testTranslateLine(self):
    android_abi = 'test-abi'
    apk_translator = MockApkTranslator(_TEST_APK_LIBS)
    mem_map = symbol_utils.MemoryMap(android_abi)
    for line, expected_line in zip(_TEST_MEMORY_MAP.splitlines(),
                                   _EXPECTED_TEST_MEMORY_MAP.splitlines()):
      self.assertEqual(mem_map.TranslateLine(line, apk_translator),
                       expected_line)

class StackTranslatorTest(unittest.TestCase):

  def testSimpleStack(self):
    android_abi = 'test-abi32'
    mem_map = symbol_utils.MemoryMap(android_abi)
    mem_map.ParseLines(_TEST_MEMORY_MAP)
    apk_translator = MockApkTranslator(_TEST_APK_LIBS)
    stack_translator = symbol_utils.StackTranslator(android_abi, mem_map,
                                                    apk_translator)
    input_stack = _TEST_STACK.splitlines()
    expected_stack = _EXPECTED_STACK.splitlines()
    self.assertEqual(len(input_stack), len(expected_stack))
    for stack_line, expected_line in zip(input_stack, expected_stack):
      new_line = stack_translator.TranslateLine(stack_line)
      self.assertEqual(new_line, expected_line)


class MockSymbolResolver(symbol_utils.SymbolResolver):

  # A regex matching a symbol definition as it appears in a test symbol file.
  # Format is: <hex-offset> <whitespace> <symbol-string>
  _RE_SYMBOL_DEFINITION = re.compile(
      r'(?P<offset>[0-9a-f]+)\s+(?P<symbol>.*)')

  def __init__(self):
    super(MockSymbolResolver, self).__init__()
    self._map = collections.defaultdict(dict)

  def AddTestLibrarySymbols(self, lib_name, offsets_map):
    """Add a new test entry for a given library name.

    Args:
      lib_name: Library name (e.g. 'libfoo.so')
      offsets_map: A mapping from offsets to symbol info strings.
    """
    self._map[lib_name] = offsets_map

  def ReadTestFile(self, file_path, lib_name):
    """Read a single test symbol file, matching a given library.

    Args:
      file_path: Input file path.
      lib_name: Library name these symbols correspond to (e.g. 'libfoo.so')
    """
    with open(file_path) as f:
      for line in f.readlines():
        line = line.rstrip()
        m = MockSymbolResolver._RE_SYMBOL_DEFINITION.match(line)
        if m:
          offset = int(m.group('offset'))
          symbol = m.group('symbol')
          self._map[lib_name][offset] = symbol

  def ReadTestFilesInDir(self, dir_path, file_suffix):
    """Read all symbol test files in a given directory.

    Args:
      dir_path: Directory path.
      file_suffix: File suffix used to detect test symbol files.
    """
    for filename in os.listdir(dir_path):
      if filename.endswith(file_suffix):
        lib_name = filename[:-len(file_suffix)]
        self.ReadTestFile(os.path.join(dir_path, filename), lib_name)

  def FindSymbolInfo(self, device_path, device_offset):
    """Implement SymbolResolver.FindSymbolInfo."""
    lib_name = os.path.basename(device_path)
    offsets = self._map.get(lib_name)
    if not offsets:
      return None

    return offsets.get(device_offset)


class BacktraceTranslatorTest(unittest.TestCase):

  def testEmpty(self):
    android_abi = 'test-abi'
    apk_translator = MockApkTranslator()
    backtrace_translator = symbol_utils.BacktraceTranslator(android_abi,
                                                            apk_translator)
    self.assertTrue(backtrace_translator)

  def testFindLibraryOffsets(self):
    android_abi = 'test-abi'
    apk_translator = MockApkTranslator(_TEST_APK_LIBS)
    backtrace_translator = symbol_utils.BacktraceTranslator(android_abi,
                                                            apk_translator)
    input_backtrace = _EXPECTED_BACKTRACE.splitlines()
    expected_lib_offsets_map = _EXPECTED_BACKTRACE_OFFSETS_MAP
    offset_map = backtrace_translator.FindLibraryOffsets(input_backtrace)
    for lib_path, offsets in offset_map.iteritems():
      self.assertTrue(lib_path in expected_lib_offsets_map,
                      '%s is not in expected library-offsets map!' % lib_path)
      sorted_offsets = sorted(offsets)
      sorted_expected_offsets = sorted(expected_lib_offsets_map[lib_path])
      self.assertEqual(sorted_offsets, sorted_expected_offsets,
                       '%s has invalid offsets %s expected %s' % (
                          lib_path, sorted_offsets, sorted_expected_offsets))

  def testTranslateLine(self):
    android_abi = 'test-abi'
    apk_translator = MockApkTranslator(_TEST_APK_LIBS)
    backtrace_translator = symbol_utils.BacktraceTranslator(android_abi,
                                                            apk_translator)
    input_backtrace = _TEST_BACKTRACE.splitlines()
    expected_backtrace = _EXPECTED_BACKTRACE.splitlines()
    self.assertEqual(len(input_backtrace), len(expected_backtrace))
    for trace_line, expected_line in zip(input_backtrace, expected_backtrace):
      line = backtrace_translator.TranslateLine(trace_line,
                                                MockSymbolResolver())
      self.assertEqual(line, expected_line)


if __name__ == '__main__':
  unittest.main()
